经过一番思考,可以发现原问题等价于选出\(2y\)个叶子,使得他们两两之间路径的并包含\(x\)且边权和最大(如果\(2y\ge\)总叶子个数则直接输出所有边权值之和).这是因为你可以通过让\(2y\)个叶子两两匹配得到\(y\)条路径,然后\(y\)条路径的并也就是这些叶子两两之间路径的并
先考虑暴力(?),对于一个\(x\)依次算出\(y=1,2,3...\)的答案.\(y=1\)时,答案就是把\(x\)作为根时,最大的 两个不同子树到根的最长链之和的.可以发现这两条最长链中有一条的端点一定是树直径的两端点之一,并且这条链一定经过直径的中点.如果我们把树的根改为树直径的中点,那么询问\((x,1)\)的答案就是\(x\)到直径两端点的距离最大值加\(x\)子树内到根最长链长度
然后考虑\(y\)每次加\(1\),贪心的考虑就是每次选出两个叶子使得路径并的增加量最大.由于每次选的叶子都是使得答案增量最大的,且选完后会使得一些其他叶子的贡献减少,如果我们对于 删去\(y=1\)时选的链剩下的树 做长链剖分,可以发现每次选的叶子代表的都是剩余的最长的长链,所以对于\(x\),只要把所有长链长度丢进一个线段树,就可以通过线段树二分的手段求出前\(k\)大的长度之和
每次长链剖分是不行的.这里我们先对以直径中点为根的树长链剖分,然后考虑其他的点.一个点\(x\)会先删掉\(y=1\)时的链,这条不在长链集合中的链一定是原来若干条长链的前缀+\(x\)所在长链+直径对应两个长链之一.我们dfs整棵树,如果点\(x\)和父亲在一条长链上,那么他们对应长链集合不变,否则会加入父亲长链剩余的下半部分,以及删去\(x\)所在长链,所以这里可以使用可持久化线段树来维护
#include<bits/stdc++.h>
#define LL long long
#define db long double
using namespace std;
const int N=1e5+10,mod=998244353;
int rd()
{
int x=0,w=1;char ch=0;
while(ch<'0'||ch>'9'){if(ch=='-') w=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=x*10+(ch^48);ch=getchar();}
return x*w;
}
int to[N<<1],nt[N<<1],w[N<<1],hd[N],tot=1;
void adde(int x,int y,int z)
{
++tot,to[tot]=y,nt[tot]=hd[x],w[tot]=z,hd[x]=tot;
++tot,to[tot]=x,nt[tot]=hd[y],w[tot]=z,hd[y]=tot;
}
int s1[N*70],s2[N*70],ch[N*70][2],rt[N],tt,vl=1,vr=1e8;
void inst(int o1,int o2,int x,int dx)
{
s1[o1]=s1[o2]+dx,s2[o1]=s2[o2]+x*dx;
int l=vl,r=vr;
while(l<r)
{
int mid=(l+r)>>1;
if(x<=mid)
{
ch[o1][0]=++tt,ch[o1][1]=ch[o2][1];
o1=ch[o1][0],o2=ch[o2][0];
r=mid;
}
else
{
ch[o1][0]=ch[o2][0],ch[o1][1]=++tt;
o1=ch[o1][1],o2=ch[o2][1];
l=mid+1;
}
s1[o1]=s1[o2]+dx,s2[o1]=s2[o2]+x*dx;
}
}
int a1,a2;
int q1(int o,int l,int r,int k)
{
if(l==r) return l;
int mid=(l+r)>>1;
if(s1[ch[o][1]]<k)
{
a1+=s1[ch[o][1]],a2+=s2[ch[o][1]];
return q1(ch[o][0],l,mid,k-s1[ch[o][1]]);
}
else return q1(ch[o][1],mid+1,r,k);
}
int n,q,dt[N],r1,r2,fa[N],fw[N],de[N],dp[N],hs[N],top[N],zl[N],fy;
void dfs0(int x)
{
dt[x]=max(dt[x],de[x]);
if(de[r1]<de[x]) r1=x;
for(int i=hd[x];i;i=nt[i])
{
int y=to[i];
if(y==fa[x]) continue;
fa[y]=x,de[y]=de[x]+w[i],dfs0(y);
}
}
void dfs1(int x)
{
dp[x]=de[x];
for(int i=hd[x];i;i=nt[i])
{
int y=to[i];
if(y==fa[x]) continue;
fa[y]=x,fw[y]=w[i],de[y]=de[x]+w[i],dfs1(y);
if(dp[y]>dp[x]) dp[x]=dp[y],hs[x]=y;
}
}
void dfs2(int x)
{
if(hs[x]) top[hs[x]]=top[x],dfs2(hs[x]);
for(int i=hd[x];i;i=nt[i])
{
int y=to[i];
if(y==fa[x]||y==hs[x]) continue;
top[y]=y,dfs2(y);
}
}
void dfs3(int x)
{
for(int i=hd[x];i;i=nt[i])
{
int y=to[i];
if(y==fa[x]) continue;
rt[y]=rt[x];
if(y!=hs[x])
{
int las=rt[y];
inst(rt[y]=++tt,las,x==r1?zl[fy]:dp[x]-de[x],1);
las=rt[y];
inst(rt[y]=++tt,las,zl[y],-1);
}
dfs3(y);
}
}
int main()
{
//////
n=rd(),q=rd();
for(int i=1;i<n;++i)
{
int x=rd(),y=rd(),z=rd();
adde(x,y,z);
}
dfs0(1);
r2=r1,r1=0,fa[r2]=de[r2]=0,dfs0(r2);
r2=r1,r1=0,fa[r2]=de[r2]=0,dfs0(r2);
int nl=de[r1];
while(fa[r1]!=r2&&de[r1]>nl-de[r1]) r1=fa[r1];
fa[r1]=de[r1]=0;
dfs1(r1),top[r1]=r1,dfs2(r1);
for(int i=1;i<=n;++i)
if(top[i]==i)
{
int las=rt[0];
inst(rt[0]=++tt,las,zl[i]=dp[i]-de[i]+fw[i],1);
}
int nm=s1[rt[0]],zans=s2[rt[0]],las;
inst(rt[r1]=++tt,rt[0],zl[r1],-1),dt[r1]=nl;
las=rt[r1],inst(rt[r1]=++tt,las,nl-zl[r1],-1);
for(int i=1;i<=n;++i)
if(i!=r1) dt[i]+=dp[i]-de[i];
for(int i=hd[r1];i;i=nt[i])
{
int y=to[i];
if(y==hs[r1]) continue;
fy=dp[fy]>dp[y]?fy:y;
}
dfs3(r1);
int ans=0;
while(q--)
{
int x=(rd()+ans-1)%n+1,y=((rd()+ans-1)%n+1)*2;
if(y>=nm) ans=zans;
else
{
y-=2,ans=dt[x];
a1=a2=0;
int nv=q1(rt[x],vl,vr,y);
ans+=a2+(y-a1)*nv;
}
printf("%d\n",ans);
}
return 0;
}