HNOI2014 世界树
我们每次询问的关键点个数是有限的,实树上很多节点的情况实际是一样的,我们可以直接一起处理。
那么我们要构建一个叫虚树的东西。只把和这几个点有关系的点建入树中,没有在虚树中的点直接用size统计答案即可
虚树中要放入关键点以及dfs序相序关键点的lca。构建虚树的方法:用单调栈维护树的最右链,每加入一个点,把之前非父亲的点弹出,然后和父亲连边即可
然后我们在虚树上跑最短路,找出离每个点最近的点,
接着对于虚树的每一条边,
1.如果两端点的最近点相同,那么这条边上的最近点均相同,直接计入答案即可
2.不同,则在边上找一个分界点,然后用1的情况处理即可
#include<cstdio> #include<cstdlib> #include<algorithm> #include<cmath> #include<cstring> #include<queue> using namespace std; struct dd{ int dis,x; bool operator < (dd a) const { return dis>a.dis; } dd(int a=0,int b=0){ dis=a; x=b; } }; priority_queue<dd> H; int num[300011],a[300011],ys[300011],stk[300011],ans[300011]; int t,T,tc,ts,tot,tt,top,n,m,i,j,x,z,root; int dfn[300011],next[600011],y[600011],g[300011],dep[300011]; int G[300011],Next[600011],Y[600011],len[600011]; int fa[300011][19],d[300011],near[300011],rank[300011],size[300011]; bool vis[300011]; bool cmp(int a,int b) { return dfn[a]<dfn[b]; } void star(int i,int j) { tt++; next[tt]=g[i]; g[i]=tt; y[tt]=j; } void stt(int i,int j,int k) { tc++; Next[tc]=G[i]; G[i]=tc; Y[tc]=j; len[tc]=k; } void dfs(int x) { int j,k; dfn[x]=++tot; size[x]=1; j=g[x]; while(j!=0){ k=y[j]; if(k!=fa[x][0]){ fa[k][0]=x; dep[k]=dep[x]+1; dfs(k); size[x]+=size[k]; } j=next[j]; } } int get(int x,int z) { int i,l,e; if(dep[x]<dep[z])swap(x,z); l=dep[x]-dep[z]; e=0; while(l){ if(l%2==1)x=fa[x][e]; e++; l/=2; } if(x==z)return x; for(i=18;i>=0;i--)if(fa[x][i]!=fa[z][i]){ x=fa[x][i]; z=fa[z][i]; } return fa[x][0]; } void Buildtree() { int i; sort(num+1,num+1+t,cmp); T=t; for(i=1;i<t;i++)num[++T]=get(num[i],num[i+1]); sort(num+1,num+1+T,cmp); ts=0; for(i=1;i<=T;i++)if(num[i]!=num[i-1]){ ts++; a[ts]=num[i]; } memset(G,0,sizeof(G)); tc=0; top=0; root=0; for(i=1;i<=ts;i++){ while(top&&!(dfn[a[i]]>=dfn[stk[top]]&&dfn[a[i]]<dfn[stk[top]]+size[stk[top]]))top--; if(top){ stt(stk[top],a[i],dep[a[i]]-dep[stk[top]]); stt(a[i],stk[top],dep[a[i]]-dep[stk[top]]); } stk[++top]=a[i]; if(root==0||dep[a[i]]<dep[root])root=a[i]; } } void Dij() { dd tp; int j,k,i; while(!H.empty())H.pop(); memset(near,0,sizeof(near)); memset(d,127,sizeof(d)); memset(vis,false,sizeof(vis)); for(i=1;i<=t;i++){ d[ys[i]]=0; near[ys[i]]=ys[i]; vis[ys[i]]=true; H.push(dd(0,ys[i])); } while(!H.empty()){ tp=H.top(); H.pop(); j=G[tp.x]; while(j!=0){ k=Y[j]; if(d[tp.x]+len[j]<d[k]||(d[tp.x]+len[j]==d[k]&&near[tp.x]<near[k])){ d[k]=d[tp.x]+len[j]; near[k]=near[tp.x]; if(!vis[k]){ vis[k]=true; H.push(dd(d[k],k)); } } j=Next[j]; } vis[tp.x]=false; } } int jump(int x,int z) { int e; e=0; while(z){ if(z%2==1)x=fa[x][e]; z/=2; e++; } return x; } bool check(int x,int z,int ds) { int dz,dx,dt; dt=jump(x,ds); dx=d[x]+dep[x]-dep[dt]; dz=d[z]+dep[dt]-dep[z]; if(dx<dz||(dx==dz&&near[x]<near[z]))return true; else return false; } int ef(int x,int z,int l,int r) { int mid; while(l<=r){ mid=(l+r)/2; if(check(x,z,mid))l=mid+1; else r=mid-1; } return jump(x,r); } void Tfind(int x,int z) { int nk,fj; nk=jump(x,dep[x]-dep[z]-1); if(near[x]==near[z]){ ans[rank[near[x]]]+=size[nk]-size[x]; } else{ if(dep[x]-dep[z]-1>0){ fj=ef(x,z,1,dep[x]-dep[z]-1); ans[rank[near[x]]]+=size[fj]-size[x]; ans[rank[near[z]]]+=size[nk]-size[fj]; } } } void find(int x,int faf) { int ad,j,k,nk; if(x==root)ans[rank[near[x]]]+=n-size[x]; j=G[x]; ad=size[x]; while(j!=0){ k=Y[j]; if(k!=faf){ nk=jump(k,dep[k]-dep[x]-1); Tfind(k,x); find(k,x); ad-=size[nk]; } j=Next[j]; } ans[rank[near[x]]]+=ad; } int main() { scanf("%d",&n); for(i=1;i<n;i++){ scanf("%d%d",&x,&z); star(x,z); star(z,x); } dfs(1); for(i=1;i<=18;i++) for(j=1;j<=n;j++)fa[j][i]=fa[fa[j][i-1]][i-1]; scanf("%d",&m); for(i=1;i<=m;i++){ scanf("%d",&t); for(j=1;j<=t;j++){ scanf("%d",&num[j]); rank[num[j]]=j; ys[j]=num[j]; ans[j]=0; } ts=0; Buildtree(); Dij(); find(root,0); for(j=1;j<=t;j++)printf("%d ",ans[j]); printf("\n"); } }