Problem
Solution
我们可以先考虑怎么解决单组询问。可以把 x x x 提做根,然后每次贪心选贡献最大的叶子即可。注意到叶子被选作的贡献是可以确定的,因为选叶子的顺序是固定的,而这个恰好对应这这棵树的长链剖分,选某个叶子的贡献就是它的长链的长度。那么我们就得到了一个优秀的 O ( n 2 log n + m ) O(n^2\log n+m) O(n2logn+m) 算法啦!
这个算法的瓶颈在于对每个询问的
x
x
x 都要换根建树,注意到选的叶子中必定至少有直径的一端,那么我们就可以对这两个端点建树,每次询问选择
2
y
−
1
2y-1
2y−1 个叶子。
为了强制包含
x
x
x,有两种情况,一种是不选最短的叶子,另一种是在
x
x
x 的某个
l
c
a
lca
lca 处转角。第一种情况比较好解决。第二种情况就是要求下式:
max i ≤ 2 y − 1 { − d i s [ l e a f i ] + d i s [ l c a ( x , l e a f i ) ] } \max_{i\leq 2y-1}\{-dis[leaf_i]+dis[lca(x,leaf_i)]\} i≤2y−1max{−dis[leafi]+dis[lca(x,leafi)]}
改成枚举 l c a lca lca 即可得到
max a n c r a n k [ f a r [ a n c ] ] ≤ 2 y − 1 { − d i s [ f a r [ a n c ] ] + d i s [ a n c ] } \max_{anc}^{rank[far[anc]]\leq 2y-1}\{-dis[far[anc]]+dis[anc]\} ancmaxrank[far[anc]]≤2y−1{−dis[far[anc]]+dis[anc]}
仔细观察,发现祖先的 r a n k rank rank是单调递减的,大括号里的式子也是单调递减的,那么直接倍增即可找到最优的祖先。
时间复杂度 O ( ( n + m ) log n ) O((n+m)\log n) O((n+m)logn)。
Code
#include <algorithm>
#include <cstring>
#include <cstdio>
using namespace std;
typedef long long ll;
const int maxn=100010;
template <typename Tp> inline int getmin(Tp &x,Tp y){return y<x?x=y,1:0;}
template <typename Tp> inline int getmax(Tp &x,Tp y){return y>x?x=y,1:0;}
template <typename Tp> inline void read(Tp &x)
{
x=0;int f=0;char ch=getchar();
while(ch!='-'&&(ch<'0'||ch>'9')) ch=getchar();
if(ch=='-') f=1,ch=getchar();
while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
if(f) x=-x;
}
struct data{int v,w,nxt;}edge[maxn<<1];
int n,m,p,rt,ans,head[maxn],dis[maxn],cp[maxn];
int cmp(const int &x,const int &y){return cp[x]>cp[y];}
struct Tree{
int cnt,lf[maxn],sum[maxn],id[maxn],val[maxn],rk[maxn];
int hs[maxn],far[maxn],dis[maxn],f[17][maxn];
void dfs1(int x)
{
int fg=1;
for(int i=1;i<17;i++) f[i][x]=f[i-1][f[i-1][x]];
for(int i=head[x];i;i=edge[i].nxt)
if(edge[i].v^f[0][x])
{
fg=0;f[0][edge[i].v]=x;dis[edge[i].v]=dis[x]+edge[i].w;
dfs1(edge[i].v);
if(dis[far[hs[x]]]<dis[far[edge[i].v]])
hs[x]=edge[i].v,far[x]=far[edge[i].v];
}
if(fg){lf[++cnt]=x;far[x]=x;}
}
void dfs2(int x,int v)
{
val[x]=v;
for(int i=head[x];i;i=edge[i].nxt)
if(edge[i].v^f[0][x])
{
if(edge[i].v^hs[x]) dfs2(edge[i].v,edge[i].w);
else dfs2(edge[i].v,v+edge[i].w);
}
}
void init(int rt)
{
dfs1(rt);
dfs2(rt,0);
memmove(cp,val,sizeof(val));
sort(lf+1,lf+cnt+1,cmp);
for(int i=1;i<=cnt;i++) rk[lf[i]]=i,sum[i]=sum[i-1]+val[lf[i]];
for(int i=1;i<=n;i++) id[i]=rk[far[i]];
}
int query(int x,int y)
{
if(y>=cnt) return sum[cnt];
if(y>=id[x]) return sum[y];
int t=far[x];
for(int i=16;~i;i--) if(f[i][x]&&id[f[i][x]]>y) x=f[i][x];
x=f[0][x];
return sum[y]+dis[t]-dis[x]-min(dis[far[x]]-dis[x],val[lf[y]]);
}
}A,B;
void insert(int u,int v,int w)
{
edge[++p]=(data){v,w,head[u]};head[u]=p;
edge[++p]=(data){u,w,head[v]};head[v]=p;
}
void dfs(int x,int pre)
{
if(dis[x]>dis[rt]) rt=x;
for(int i=head[x];i;i=edge[i].nxt)
if(edge[i].v^pre)
{
dis[edge[i].v]=dis[x]+edge[i].w;
dfs(edge[i].v,x);
}
}
void input()
{
int u,v,w;
read(n);read(m);
for(int i=1;i<n;i++)
{
read(u);read(v);read(w);
insert(u,v,w);
}
dfs(1,1);
A.init(rt);
dis[rt]=0;dfs(rt,rt);
B.init(rt);
}
int main()
{
int x,y;
input();
while(m--)
{
read(x);read(y);
x=(x+ans-1)%n+1;y=(y+ans-1)%n+1;
y=(y<<1)-1;
printf("%d\n",ans=max(A.query(x,y),B.query(x,y)));
}
return 0;
}