2026-01-29~30 hetao1733837 的刷题记录
01-29
LGP5563 [Celeste-B] No More Running
原题链接:[Celeste-B] No More Running
分析
她好像有npy了......
我去学浦东了......
她好像原谅我了
理解为一个点,选一条以他为路径端点的树上路径权值模 mod \operatorname{mod} mod 的最大值。怎么分治呢?哦,这也是点对!所以分治......
她好像还单着。
但是,这个取模太阴了......思考一下......
她为啥不回我消息?
我在干什么?我什么要在中午在机房看 B 站?
看这个会不会有什么启发?

思考一下......我么们考虑 merge......找到子树重心......然后......理论上我只能选一边啊......还是看题解吧......我们预处理模意义下树上两点路径距离,发现合并时最多进行一次取模,分讨是否取模即可。没了......
下午......思考一下什么时候可以写代码。那就到晚上了......彳亍......
她为什么还不回我?
其实再等一分钟就回我了......
正解
cpp
#include <bits/stdc++.h>
using namespace std;
const int N = 100005;
int n, mod, rt, mn, sum;
int sz[N], d[N], ans[N];
vector<pair<int, int>> e[N];
bool vis[N];
multiset<int> s;
void dfs1(int u, int fa){
int mx = 0;
sz[u] = 1;
for (auto tmp : e[u]){
int v = tmp.first;
if (v == fa || vis[v])
continue;
dfs1(v, u);
sz[u] += sz[v];
mx = max(mx, sz[v]);
}
mx = max(mx, sum - sz[u]);
if (mn > mx){
mn = mx;
rt = u;
}
}
void dfs2(int u, int fa){
s.insert(d[u]);
for (auto tmp : e[u]){
int v = tmp.first;
if (v == fa || vis[v])
continue;
d[v] = (d[u] + tmp.second) % mod;
dfs2(v, u);
}
}
bool flag;
void modify(int u, int fa){
if (flag)
s.insert(d[u]);
else
s.erase(s.find(d[u]));
for (auto tmp : e[u]){
int v = tmp.first;
if (v == fa || vis[v])
continue;
modify(v, u);
}
}
void got(int u, int fa){
auto it = s.upper_bound(mod - d[u] - 1);
if (it != s.begin()){
ans[u] = max(ans[u], d[u] + *(--it));
}
ans[u] = max(ans[u], (d[u] + *s.rbegin()) % mod);
for (auto tmp : e[u]){
int v = tmp.first;
if (v == fa || vis[v])
continue;
got(v, u);
}
}
void solve(int u){
mn = 0x7f7f7f7f;
dfs1(u, 0);
dfs1(rt, 0);
d[rt] = 0;
dfs2(rt, 0);
for (auto tmp : e[rt]){
int v = tmp.first;
if (vis[v])
continue;
flag = false;
modify(v, rt);
got(v, rt);
flag = true;
modify(v, rt);
}
ans[rt] = max(ans[rt], *s.rbegin());
flag = false;
modify(rt, 0);
vis[rt] = true;
for (auto tmp : e[rt]){
int v = tmp.first;
if (vis[v])
continue;
sum = sz[v];
solve(v);
}
}
signed main(){
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin >> n >> mod;
for (int i = 1, u, v, w; i < n; i++){
cin >> u >> v >> w;
e[u].push_back({v, w});
e[v].push_back({u, w});
}
sum = n;
solve(1);
for (int i = 1; i <= n; i++)
cout << ans[i] << '\n';
}
01-30
CF2126G2 Big Wins! (hard version)
原题链接:CF2126G2 Big Wins! (hard version)
分析
思考一下......这个东西的 tag 这么猎奇?二分查找、数据结构、分治、启发式、树、双指针......
样例太水了......根本盯不出来性质......看起来......那也不对啊......思索一下......
我似乎知道某个求中位数的 Trick ,不幸的是,已经想不起来了......太 ** 压抑了......
最小值很好确定, S T ST ST 表就可以......卡在中位数了......
第一步很典,固定最小值,向右逐步扩展,则呈双指针状物。
然后,拿一个单调栈确定包含此最小值的最大的区间。
哦,然后就是一个很典的东西了,将大于等于的记为1,小于的记为-1,看和就可以了 。
那么,问题转化为类似求前缀最大值、后缀最大值、区间和的杂糅,故可使用线段树。
似乎写完了......我不知道......看看代码,自己试着写一写吧......
古生物真有意思......
老外不会用结构体?
正解
cpp
#include <bits/stdc++.h>
using namespace std;
const int N = 200005;
int n, a[N];
struct node{
int mx, pre, suf, sum;
};
node tr[N << 2];
struct segtree{
void pushup(int p){
tr[p].mx = max(tr[p << 1].mx, tr[p << 1 | 1].mx);
tr[p].pre = max(tr[p << 1].pre, tr[p << 1].sum + tr[p << 1 | 1].pre);
tr[p].suf = max(tr[p << 1 | 1].suf, tr[p << 1].suf + tr[p << 1 | 1].sum);
tr[p].mx = max({tr[p].mx, tr[p].pre, tr[p].suf});
tr[p].sum = tr[p << 1].sum + tr[p << 1 | 1].sum;
}
void build(int p, int l, int r){
if (l == r){
tr[p].mx = tr[p].pre = tr[p].suf = tr[p].sum = 0;
return ;
}
int mid = (l + r) >> 1;
build(p << 1, l, mid);
build(p << 1 | 1, mid + 1, r);
pushup(p);
}
node merge(node x, node y){
int mx = max(x.mx, y.mx);
int pre = max(x.pre, x.sum + y.pre);
int suf = max(y.suf, x.suf + y.sum);
mx = max({mx, pre, suf});
int sum = x.sum + y.sum;
return {mx, pre, suf, sum};
}
void modify(int p, int l, int r, int s, int val){
if (l == r){
tr[p].mx = max(val, 0);
tr[p].pre = max(val, 0);
tr[p].suf = max(val, 0);
tr[p].sum = val;
return ;
}
int mid = (l + r) >> 1;
if (s <= mid)
modify(p << 1, l, mid, s, val);
else
modify(p << 1 | 1, mid + 1, r, s, val);
pushup(p);
}
node query(int p, int l, int r, int s, int t){
if (l > r || s > t){
return {0, 0, 0, 0};
}
if (l == s && r == t){
return tr[p];
}
int mid = (l + r) >> 1;
if (t <= mid)
return query(p << 1, l, mid, s, t);
else if (s > mid)
return query(p << 1 | 1, mid + 1, r, s, t);
else
return merge(query(p << 1, l, mid, s, mid),
query(p << 1 | 1, mid + 1, r, mid + 1, t));
}
}T;
int solve(int n, vector<int> arr){
int m = 0;
for (int i = 1; i <= n; i++){
a[i] = arr[i - 1];
m = max(m, a[i]);
}
vector<int> ind[m + 1];
for (int i = 1; i <= n; i++){
ind[a[i]].push_back(i);
}
stack<int> s;
s.push(0);
a[0] = 0xc0c0c0c0;
int l[n + 1];
for (int i = 1; i <= n; i++){
while (a[s.top()] >= a[i]){
s.pop();
}
l[i] = s.top() + 1;
s.push(i);
}
a[n + 1] = 0xc0c0c0c0;
while (!s.empty())
s.pop();
s.push(n + 1);
int r[n + 1];
for (int i = n; i >= 1; i--){
while (a[s.top()] >= a[i]){
s.pop();
}
r[i] = s.top() - 1;
s.push(i);
}
int med = 1;
T.build(1, 1, n);
for (int i = 1; i <= n; i++){
T.modify(1, 1, n, i, 1);
}
for (auto u : ind[1]){
T.modify(1, 1, n, u, -1);
}
int ans = 0;
for (int mn = 1; mn <= m; mn++){
for (auto u : ind[mn]){
int L = l[u], R = r[u];
while (med < m){
int left_val = (L <= u - 1) ? T.query(1, 1, n, L, u - 1).suf : 0;
int right_val = (u + 1 <= R) ? T.query(1, 1, n, u + 1, R).pre : 0;
int current = (a[u] < med ? -1 : 1);
if (left_val + right_val + current >= 0){
med++;
for (auto v : ind[med]){
T.modify(1, 1, n, v, -1);
}
}
else{
break;
}
}
}
ans = max(ans, med - mn);
}
return ans;
}
int t;
int main(){
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin >> t;
while (t--){
cin >> n;
vector<int> inp;
for (int i = 1, x; i <= n; i++){
cin >> x;
inp.push_back(x);
}
cout << solve(n, inp) << '\n';
}
return 0;
}
02-01
LGP6329 【模板】点分树 / 震波
原题链接:【模板】点分树 / 震波
分析
动态开点真的需要开这么多空间吗?
正解
cpp
#include <bits/stdc++.h>
using namespace std;
const int N = 100005;
int n, m, v[N], rt;
vector<int> e[N];
int f[N][21], de[N], sz[N], mx[N];
bool vis[N];
void dfs(int u, int pa){
f[u][0] = pa;
de[u] = de[pa] + 1;
for (auto v : e[u]){
if (v == pa)
continue;
dfs(v, u);
}
}
int lca(int x, int y){
if (de[x] < de[y])
swap(x, y);
int k = de[x] - de[y];
for (int i = 0; i <= 20; i++){
if (k & (1 << i))
x = f[x][i];
}
if (x == y)
return y;
for (int i = 20; i >= 0; i--){
if (f[x][i] != f[y][i]){
x = f[x][i];
y = f[y][i];
}
}
return f[x][0];
}
int getdis(int x, int y){
return de[x] + de[y] - 2 * de[lca(x, y)];
}
void getrt(int u, int pa, int cnt){
sz[u] = 1;
mx[u] = 0;
for (auto v : e[u]){
if (v == pa || vis[v])
continue;
getrt(v, u, cnt);
sz[u] += sz[v];
mx[u] = max(mx[u], sz[v]);
}
mx[u] = max(mx[u], cnt - sz[u]);
if (mx[u] < mx[rt])
rt = u;
}
int dfn[N];
void solve(int u, int cnt){
vis[u] = true;
for (auto v : e[u]){
if (vis[v])
continue;
rt = 0;
int tmp = (sz[v] < sz[u]) ? sz[v] : (cnt - sz[u]);
getrt(v, u, tmp);
dfn[rt] = u;
solve(rt, tmp);
}
}
struct segtree{
int root[N], ncnt = 0;
struct node{
int ls, rs, val;
}tr[N * 50];
void pushup(int p){
tr[p].val = tr[tr[p].ls].val + tr[tr[p].rs].val;
}
void modify(int &p, int l, int r, int s, int val){
if (!p)
p = ++ncnt;
if (l == r){
tr[p].val += val;
return ;
}
int mid = (l + r) >> 1;
if (s <= mid)
modify(tr[p].ls, l, mid, s, val);
else
modify(tr[p].rs, mid + 1, r, s, val);
pushup(p);
}
int query(int p, int l, int r, int s, int t){
if(!p)
return 0;
if (s <= l && r <= t)
return tr[p].val;
int mid = (l + r) >> 1;
int res = 0;
if (s <= mid)
res += query(tr[p].ls, l, mid, s, t);
if (t > mid)
res += query(tr[p].rs, mid + 1, r, s, t);
return res;
}
}T1, T2;
void change(int p, int val){
int pos = p;
while (pos){
T1.modify(T1.root[pos], 0, n - 1, getdis(pos, p), val);
if (dfn[pos]){
T2.modify(T2.root[pos], 0, n - 1, getdis(dfn[pos], p), val);
}
pos = dfn[pos];
}
}
int ask(int p, int k){
int pos = p, lst = 0, res = 0;
while (pos){
if (getdis(pos, p) > k){
lst = pos;
pos = dfn[pos];
continue;
}
res += T1.query(T1.root[pos], 0, n - 1, 0, k - getdis(pos, p));
if (lst)
res -= T2.query(T2.root[lst], 0, n - 1, 0, k - getdis(pos, p));
lst = pos;
pos = dfn[pos];
}
return res;
}
int main(){
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin >> n >> m;
for (int i = 1; i <= n; i++)
cin >> v[i];
for (int i = 1, u, v; i < n; i++){
cin >> u >> v;
e[u].push_back(v);
e[v].push_back(u);
}
dfs(1, 0);
for (int j = 1; j <= 20; j++){
for (int i = 1; i <= n; i++){
f[i][j] = f[f[i][j - 1]][j - 1];
}
}
mx[0] = 0x3f3f3f3f;
rt = 0;
getrt(1, 0, n);
solve(rt, n);
for (int i = 1; i <= n; i++){
change(i, v[i]);
}
int lst = 0;
for (int cs = 1, op, x, k, y; cs <= m; cs++){
cin >> op >> x;
if (op == 0){
cin >> k;
x ^= lst;
k ^= lst;
lst = ask(x, k);
cout << lst << '\n';
}
else{
cin >> y;
x ^= lst;
y ^= lst;
change(x, y - v[x]);
v[x] = y;
}
}
}
02-03
LGP3345 [ZJOI2015] 幻想乡战略游戏
原题链接:[ZJOI2015] 幻想乡战略游戏
分析
闪现了一个换根 DP 状物......为啥我看到了虚树......感觉这个和 lca 关系似乎很大啊......居然有树剖做法......但是,似乎点分树更好想......因为......思索一下......但是,我还是觉得......哦......难道是离线吗?但我还是想看看虚树做法......哦......原来是带权重心吗?那就是点分树了!然后......就是......上一个板子里就有一个向下跳的过程,但是会失去信息,所以,我看的题解直接做了一个预处理......没了......
正解
cpp
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 100005;
int n, Q;
vector<pair<int, int>> e[N];
map<int, int> d[N];
vector<int> tr[N];
int mx[N], sz[N], tot, rt;
bool vis[N];
void getrt(int u, int pa){
sz[u] = 1;
mx[u] = 0;
for (auto tmp : e[u]){
int v = tmp.first;
if (v == pa || vis[v])
continue;
getrt(v, u);
sz[u] += sz[v];
mx[u] = max(mx[u], sz[v]);
}
mx[u] = max(mx[u], tot - sz[u]);
if (mx[rt] > mx[u])
rt = u;
}
void init(int u, int pa, int ro){
sz[u] = 1;
for (auto tmp : e[u]){
int v = tmp.first, w = tmp.second;
if (v == pa || vis[v])
continue;
d[ro][v] = d[ro][u] + w;
init(v, u, ro);
sz[u] += sz[v];
}
}
int dfa[N], fa[N];
void dfs(int u){
vis[u] = 1;
d[u][u] = 0;
init(u, 0, u);
for (auto tmp : e[u]){
int v = tmp.first;
if (vis[v])
continue;
tot = sz[v];
rt = 0;
getrt(v, u);
tr[u].push_back(rt);
fa[rt] = u;
dfa[rt] = v;
dfs(rt);
}
}
int sum[N], sum1[N], sum2[N];
void moidfy(int p, int val){
int pos = p;
while (pos){
sum[pos] += val;
sum1[pos] += d[pos][p] * val;
if (fa[pos])
sum2[pos] += d[fa[pos]][p] * val;
pos = fa[pos];
}
}
int got(int p){
int res = sum1[p];
int pos = p;
while (fa[pos]){
res += sum1[fa[pos]] - sum2[pos] + d[fa[pos]][p] * (sum[fa[pos]] - sum[pos]);
pos = fa[pos];
}
return res;
}
int query(int u){
int res = got(u);
for (auto v : tr[u]){
int tmp = got(dfa[v]);
if (tmp < res)
return query(v);
}
return res;
}
signed main(){
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin >> n >> Q;
for (int i = 1, a, b, c; i < n; i++){
cin >> a >> b >> c;
e[a].push_back({b, c});
e[b].push_back({a, c});
}
mx[0] = 0x3f3f3f3f;
tot = n;
getrt(1, 0);
int cur = rt;
dfs(rt);
for (int cs = 1, U, E; cs <= Q; cs++){
cin >> U >> E;
moidfy(U, E);
cout << query(cur) << '\n';
}
}