【题解】CF526G-Spiders Evil Plan
难度很高的树上综合题。
首先我们考虑没有 \(x\) 限制,\(q=1\) 的情况。也就是我们要选出若干条路径使得并集的边权和最大。
我们选择的路径一定形成一个连通块,否则存在存在两条路径 \((x,y)\) 和 \((a,b)\) 不相交,我们可以调整为 \((x,b)\) 和 \((a,y)\)。
如果我们固定路径一端,那么另一端一定距离最远,一定是树的直径两端中的一个。所以我们以直径端点为根,那么等价于选择 \(2y-1\) 条到根的路径使得并集最大。
一个简易的贪心是每次选择最长的链,然后将链删掉。这个贪心是正确的,我们可以用费用流模型解释,因为树形结构的存在,费用流不存在退流的情况,所以贪心是正确的。
同时观察一下,每次选择最长的链,这本质上就是长链剖分的过程,剖分后按链长排序后依次选择即可。
那么接下来考虑 \(x\) 的限制。如果 \(x\) 在前 \(2y-1\) 条链里面,那么可以直接得到答案。
如果不在里面,我们仍然用调整法将答案调整至最优。我们先找到 \(x\) 最近的在前 \(2y-2\) 条链里面的祖先 \(y\),选择将第 \(2y-1\) 条链替换成 \((x,y)\),或者找到在前 \(2y-1\) 条链里面的祖先 \(z\),将 \(z\) 下面接的单链替换成经过 \(x\) 的最长链。
距离可以倍增实现,时间复杂度 \(\mathcal{O}(N\log N)\)。
#define N 100005
int n, q, t, d[N]; vector<Pr>e[N];
void dfs(int x,int fa){
go(y, e[x])if(y.fi != fa)d[y.fi] = d[x] + y.se, dfs(y.fi, x);
}
struct node{
int f[N][17], d[N], ds[N], son[N], dfn[N], idx, rt, mat[N], s[N]; Pr w[N];
void dfs(int x,int fa){
d[x] = 0;
go(y, e[x])if(y.fi != fa){
ds[y.fi] = ds[x] + y.se, dfs(y.fi, x);
if(d[y.fi] + y.se >= d[x])son[x] = y.fi, d[x] = d[y.fi] + y.se;
}
}
void dfs2(int x,int fa,int id){
f[x][0] = fa, dfn[x] = id;
rp(i, t)f[x][i] = f[f[x][i - 1]][i - 1];
if(!son[x])return;
dfs2(son[x], x, id);
go(y, e[x])if(y.fi != fa && y.fi != son[x])
w[++idx].fi = d[y.fi] + y.se, dfs2(y.fi, x, idx);
}
void init(int x){
dfs(rt = x, 0);
w[++idx].fi = d[rt], dfs2(rt, 0, idx);
rp(i, idx)w[i].se = i;
sort(w + 1, w + idx + 1, [](Pr x, Pr y){return x > y;});
rp(i, idx)mat[w[i].se] = i, s[i] = s[i - 1] + w[i].fi;
}
int ask(int x,int y){
y = y + y - 1;
if(mat[dfn[x]] <= y)return s[min(y, idx)];
int v = x;
pre(i, t, 0)if(mat[dfn[f[v][i]]] > y)v = f[v][i];
v = f[v][0]; int ans = s[y] - d[v] + ds[x] - ds[v] + d[x];
v = x; pre(i, t, 0)if(mat[dfn[f[v][i]]] >= y)v = f[v][i];
v = f[v][0]; cmx(ans, s[y - 1] + d[x] + ds[x] - ds[v]);
return ans;
}
}A, B;
int main() {
read(n, q), t = log2(n);
rp(i, n - 1){
int x, y, z;
read(x, y, z);
e[x].pb(mp(y, z)),
e[y].pb(mp(x, z));
}
dfs(1, 0); int X = 1, Y = 1;
rp(i, n)if(d[i] > d[X])X = i;
d[X] = 0, dfs(X, 0);
rp(i, n)if(d[i] > d[Y])Y = i;
A.init(X), B.init(Y);
int lst = 0;
while(q--){
int x, y; read(x, y);
x = (x + lst - 1) % n + 1;
y = (y + lst - 1) % n + 1;
printf("%d\n", lst = max(A.ask(x, y), B.ask(x, y)));
}
return 0;
}