正题
题目链接:https://www.luogu.com.cn/problem/P3233
题目大意
nnn个点的一棵树,mmm次选出一些点作为关键点。每个树上的点会对最近的关键点做贡献,求每个关键点的贡献。
解题思路
显然是虚树,考虑如何在虚树上求贡献,我们发现难处理的是虚树上每一条链的贡献,因为这个链上代表了原树上的多个点。我们可以先对于虚树上每一个点求出一个nerxner_xnerx表示距离它最近的关键点。
然后我们对于一条边,我们可以计算出一个长度表示该边代表的链这个长度一下都是属于yyy的,上面的属于xxx的。然后我们可以倍增求出原树上的分界点来计算答案。
因为建虚树和求答案的logloglog是分开来的,所以时间复杂度为O((n+∑k)logn)O(\ (n+\sum k)\ \log n)O( (n+∑k) logn)
codecodecode
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=300100,T=20;
struct node{int to,next;
}a[N*2];
int n,q,k,tot,cnt,ls[N],siz[N],dep[N],dfn[N];
int ner[N],ans[N],p[N],pos[N],s[N],f[N][T+1];
void addl(int x,int y){a[++tot].to=y;a[tot].next=ls[x];ls[x]=tot;return;
}
bool cmp(int x,int y){return dfn[x]<dfn[y];}
bool cMp(int x,int y){return pos[x]<pos[y];}
int LCA(int x,int y){if(dep[x]>dep[y])swap(x,y);for(int i=T;i>=0;i--)if(dep[f[y][i]]>=dep[x])y=f[y][i];if(x==y)return x;for(int i=T;i>=0;i--)if(f[x][i]!=f[y][i])x=f[x][i],y=f[y][i];return f[x][0];
}
int gup(int x,int k){for(int i=T;i>=0;i--)if((k>>i)&1)x=f[x][i];return x;
}
void Add(int x){if(!cnt){s[++cnt]=x;return;}int lca=LCA(x,s[cnt]);while(cnt>1&&dep[s[cnt-1]]>dep[lca])addl(s[cnt-1],s[cnt]),cnt--;if(dep[s[cnt]]>dep[lca])addl(lca,s[cnt]),cnt--;if((!cnt)||(s[cnt]!=lca))s[++cnt]=lca;s[++cnt]=x;return;
}
void dfs1(int x,int fa){siz[x]=1;dep[x]=dep[fa]+1;dfn[x]=++cnt;for(int i=ls[x];i;i=a[i].next){int y=a[i].to;if(y==fa)continue;f[y][0]=x;dfs1(y,x);siz[x]+=siz[y];}return;
}
int get_dis(int x,int y)
{return dep[x]+dep[y]-2*dep[LCA(x,y)];}
void dfs_son(int x,int fa){if(pos[x])ner[x]=x;else ner[x]=0;for(int i=ls[x];i;i=a[i].next){int y=a[i].to,w=dep[y]-dep[x];if(y==fa)continue;dfs_son(y,x);if(!ner[x]||dep[ner[y]]<dep[ner[x]]||(dep[ner[y]]==dep[ner[x]]&&ner[y]<ner[x]))ner[x]=ner[y];}return;
}
void dfs_father(int x,int fa){for(int i=ls[x];i;i=a[i].next){int y=a[i].to,w=dep[y]-dep[x];if(y==fa)continue;int zx=get_dis(x,ner[x]),zy=get_dis(y,ner[y]);if(!ner[y]||zy>zx+w||(zy==zx+w&&ner[x]<ner[y]))ner[y]=ner[x]; dfs_father(y,x);}return;
}
void solve(int x,int fa){ans[ner[x]]+=siz[x]; for(int i=ls[x];i;i=a[i].next){int y=a[i].to,w=dep[y]-dep[x]-1,gd=gup(y,w);if(y==fa)continue;if(ner[x]==ner[y])ans[ner[x]]+=siz[gd]-siz[y];else{int zx=get_dis(x,ner[x]),zy=get_dis(y,ner[y]);int k=(zx+zy+w)/2;if((zx+zy+w)&1)k+=ner[y]<ner[x];k-=zy;int pw=gup(y,k);if(k<=0)ans[ner[x]]+=siz[gd]-siz[y];else if(k>w)ans[ner[y]]+=siz[gd]-siz[y];else ans[ner[y]]+=siz[pw]-siz[y],ans[ner[x]]+=siz[gd]-siz[pw];}ans[ner[x]]-=siz[gd];solve(y,x);}ls[x]=0;return;
}
int main()
{scanf("%d",&n);for(int i=1;i<n;i++){int x,y;scanf("%d%d",&x,&y);addl(x,y);addl(y,x);}dfs1(1,1);for(int j=1;j<=T;j++)for(int i=1;i<=n;i++)f[i][j]=f[f[i][j-1]][j-1];memset(ls,0,sizeof(ls));scanf("%d",&q);while(q--){scanf("%d",&k);tot=cnt=0;for(int i=1;i<=k;i++)scanf("%d",&p[i]),pos[p[i]]=i;sort(p+1,p+1+k,cmp);if(p[1]!=1)s[++cnt]=1;for(int i=1;i<=k;i++)Add(p[i]);while(cnt>1)addl(s[cnt-1],s[cnt]),cnt--;dfs_son(1,1);dfs_father(1,1);solve(1,1);sort(p+1,p+1+k,cMp);for(int i=1;i<=k;i++)printf("%d ",ans[p[i]]),ans[p[i]]=pos[p[i]]=0;putchar('\n');}return 0;
}