1. 程式人生 > >P3233 [HNOI2014]世界樹

P3233 [HNOI2014]世界樹

傳送門

看到指定的總節點數小於等於 300000 就知道要搞虛樹了

考慮如何在虛樹確定每個議事處控制的節點數量

可以兩遍dfs

第一遍求兒子對父親的影響,第二遍求父親對兒子影響

注意搜尋順序,這樣就可以把影響擴充套件到其他子樹了

如圖:

初始時只有本身被影響

經過第一遍dfs後父親被影響:

經過第二遍dfs後兒子被影響:

這樣就可以考慮到所有情況了

 然後對於虛樹上的每一條邊,考慮它的貢獻

對於虛樹上的一條邊 $(u,v)$ ($u$ 是父節點,$u,v$被同一點控制),我們可以在原樹上從$v$倍增跳到離$u$最近的節點$p_1$

那麼設原樹上的節點子樹大小為 $sz$,那麼虛樹上那條邊的貢獻就是$sz[p_1]-sz[v]$

如果$u,v$不被同一點控制,那麼中間肯定有一個分界點,我們也可以倍增從$v$在原樹上跳到分界點$p_2$

設 $bel[x]$ 存節點 x 屬於的節點

在$p_2$及以下的部分屬於$bel[v]$,對$bel[v]的$貢獻為 $sz[p_2]-sz[v]$

上面一直到 $u$ 的部分屬於 $bel[u]$,貢獻顯然為 $sz[p_1]-sz[p_2]$

對於一個節點$x$,它還有一部分子樹不在虛樹上,設它們的數量為$sur[x]$,

初始時$sur[x]=sz[x]$,然後我們每次列舉一條虛樹邊就把$sur[x]$減去那條邊的子樹大小

($sur[x]=sur[x]-sz[p_1]$)

最後的$sur[x]$就是在$x$子樹上但不在虛樹上的節點數量了

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
#include<vector>
using namespace std;
typedef long long ll;
inline int read()
{
    int x=0,f=1; char ch=getchar();
    while
(ch<'0'||ch>'9') { if(ch=='-') f=-1; ch=getchar(); } while(ch>='0'&&ch<='9') { x=(x<<1)+(x<<3)+(ch^48); ch=getchar(); } return x*f; } const int N=3e5+7; int fir[N],from[N<<1],to[N<<1],cntt;//存原樹 inline void add(int &a,int &b) { from[++cntt]=fir[a]; fir[a]=cntt; to[cntt]=b; } int n,m; int dep[N],sz[N],f[N][21],dfn[N],dfs_clock;//f是倍增陣列 void dfs(int x)//預處理各種東西 { dep[x]=dep[f[x][0]]+1; sz[x]=1; dfn[x]=++dfs_clock; for(int i=1;i<=20;i++) f[x][i]=f[f[x][i-1]][i-1]; for(int i=fir[x];i;i=from[i]) { int v=to[i]; if(dfn[v]) continue; f[v][0]=x; dfs(v); sz[x]+=sz[v]; } } inline int LCA(int x,int y)//求LCA { if(dep[x]<dep[y]) swap(x,y); for(int i=20;i>=0;i--) if(dep[f[x][i]]>=dep[y]) x=f[x][i]; if(x==y) return x; for(int i=20;i>=0;i--) if(f[x][i]!=f[y][i]) x=f[x][i],y=f[y][i]; return f[x][0]; } int st[N<<1],Top; vector <int> v[N];//存虛樹 inline void ins(int x)//插入一個節點到虛樹裡 { if(Top==1) { st[++Top]=x; return; } int lca=LCA(x,st[Top]); if(lca!=st[Top]) while(Top>1 && dfn[st[Top-1]]>=dfn[lca]) v[st[Top-1]].push_back(st[Top]),Top--; if(lca!=st[Top]) v[lca].push_back(st[Top]),st[Top]=lca; st[++Top]=x; } int cnt[N<<1],bel[N<<1],sur[N<<1]; //cnt存每個議事處控制的數量 bool pd[N];//pd判斷是否是議事處 void dfs1(int x)//第一遍dfs考慮兒子對父親的貢獻 { int len=v[x].size(); sur[x]=sz[x]; if(pd[x]) bel[x]=x;//如果它本身是議事處,那麼顯然被自己控制 else bel[x]=0;//注意多組詢問,要清0 for(int i=0;i<len;i++) { int t=v[x][i]; dfs1(t);//先向下dfs再考慮兒子的影響 if(!bel[x]) { bel[x]=bel[t]; continue; }//特判 int d1=dep[bel[t]]-dep[x],d2=dep[bel[x]]-dep[x]; if(d1<d2) bel[x]=bel[t];//考慮用兒子更新bel else if(d1==d2&&bel[t]<bel[x]) bel[x]=bel[t];//注意如果距離相同取編號小的 } } inline int dis(int x,int y) { return dep[x]+dep[y]-2*dep[LCA(x,y)]; }//求不在一條鏈上的兩點距離 void dfs2(int x)//第二遍dfs考慮父親對兒子的影響 { int len=v[x].size(); for(int i=0;i<len;i++) { int t=v[x][i],d1=dis(bel[x],t),d2=dis(bel[t],t); if(d1<d2) bel[t]=bel[x];//考慮用父親更新兒子 else if(d1==d2&&bel[x]<bel[t]) bel[t]=bel[x];//距離相同取編號小的 // if(t==3) cout<<bel[t]<<" "; dfs2(t);//注意此時先更新兒子再dfs } } void dp(int x)//最後dp統計貢獻 { int len=v[x].size(),p1,p2,t; for(int i=0;i<len;i++) { p1=p2=t=v[x][i]; dp(t); for(int j=20;j>=0;j--) if(dep[f[p1][j]]>dep[x]) p1=f[p1][j];//倍增找到離x最近的節點 sur[x]-=sz[p1];//更新sur if(bel[x]==bel[t]) cnt[bel[x]]+=sz[p1]-sz[t];//如果邊上兩點屬於同一議事處直接更新貢獻 else { for(int j=20;j>=0;j--)//否則倍增找到分界點 { if(dep[f[p2][j]]<=dep[x]) continue; int d1=dis(f[p2][j],bel[t]),d2=dis(f[p2][j],bel[x]); if(d1<d2) p2=f[p2][j]; else if(d1==d2&&bel[t]<bel[x]) p2=f[p2][j];//同樣如果距離一樣去編號小的 } cnt[bel[x]]+=sz[p1]-sz[p2]; cnt[bel[t]]+=sz[p2]-sz[t];//兩邊都要更新 } } cnt[bel[x]]+=sur[x];//最後貢獻再再加上sur[x] v[x].clear();//記得清空 } inline bool cmp(const int &a,const int &b) { return dfn[a]<dfn[b]; }//按dfs序排序 int d[N],dd[N]; inline void solve()//處理詢問 { int t=read(); for(int i=1;i<=t;i++) { d[i]=dd[i]=read(); pd[d[i]]=1; } sort(d+1,d+t+1,cmp); st[Top=1]=1; for(int i=(pd[1] ? 2 : 1);i<=t;i++) ins(d[i]);//插入 while(Top>1) v[st[Top-1]].push_back(st[Top]),Top--; dfs1(1); dfs2(1); dp(1); for(int i=1;i<=t;i++) { printf("%d ",cnt[dd[i]]); pd[dd[i]]=cnt[dd[i]]=0;//記得清空 } printf("\n"); } int main() { // freopen("data.in","r",stdin); // freopen("data.out","w",stdout); int a,b; n=read(); for(int i=1;i<n;i++) { a=read(),b=read(); add(a,b); add(b,a); } dfs(1); m=read(); while(m--) solve(); return 0; }