bzoj 3572: [Hnoi2014]世界樹
Description
世界樹是一棵無比巨大的樹,它伸出的枝幹構成了整個世界。在這裏,生存著各種各樣的種族和生靈,他們共同信奉著絕對公正公平的女神艾莉森,在他們的信條裏,公平是使世界樹能夠生生不息、持續運轉的根本基石。
世界樹的形態可以用一個數學模型來描述:世界樹中有n個種族,種族的編號分別從1到n,分別生活在編號為1到n的聚居地上,種族的編號與其聚居地的編號相同。有的聚居地之間有雙向的道路相連,道路的長度為1。保證連接的方式會形成一棵樹結構,即所有的聚居地之間可以互相到達,並且不會出現環。定義兩個聚居地之間的距離為連接他們的道路的長度;例如,若聚居地a和b之間有道路,b和c之間有道路,因為每條道路長度為1而且又不可能出現環,所臥a與c之間的距離為2。
出於對公平的考慮,第i年,世界樹的國王需要授權m[i]個種族的聚居地為臨時議事處。對於某個種族x(x為種族的編號),如果距離該種族最近的臨時議事處為y(y為議事處所在聚居地的編號),則種族x將接受y議事處的管轄(如果有多個臨時議事處到該聚居地的距離一樣,則y為其中編號最小的臨時議事處)。
現在國王想知道,在q年的時間裏,每一年完成授權後,當年每個臨時議事處將會管理多少個種族(議事處所在的聚居地也將接受該議事處管理)。 現在這個任務交給了以智慧著稱的靈長類的你:程序猿。請幫國王完成這個任務吧。
Input
第一行為一個正整數n,表示世界樹中種族的個數。
接下來n-l行,每行兩個正整數x,y,表示x聚居地與y聚居地之間有一條長度為1的雙
向道路。接下來一行為一個正整數q,表示國王詢問的年數。
接下來q塊,每塊兩行:
第i塊的第一行為1個正整數m[i],表示第i年授權的臨時議事處的個數。
第i塊的第二行為m[i]個正整數h[l]、h[2]、…、h[m[i]],表示被授權為臨時議事處的聚居地編號(保證互不相同)。
Output
輸出包含q行,第i行為m[i]個整數,該行的第j(j=1,2…,,m[i])個數表示第i年被授權的聚居地h[j]的臨時議事處管理的種族個數。
Sample Input
2 1
3 2
4 3
5 4
6 1
7 3
8 3
9 4
10 1
5
2
6 1
5
2 7 3 6 9
1
8
4
8 7 10 3
5
2 9 3 5 8
Sample Output
1 93 1 4 1 1
10
1 1 3 5
4 1 3 1 1
HINT
N<=300000, q<=300000,m[1]+m[2]+…+m[q]<=300000
Sourc
好久沒有打這麽刺激的題了。。。
題目中明確告訴你每個詢問有k個關鍵點,且∑k為線性,看起來就很虛樹。。。
首先把虛樹構建出來,在虛樹上通過兩邊dp,求出虛樹上所有點的歸屬點。。。
(第一遍用兒子更新父親,第二遍用父親更新兒子,樹型dp正常套路。。。)
然後考慮尚未在虛樹中出現的點,這個的話感覺講不太清楚,畫一下圖吧。。。
假設1,6是虛樹上的點,且1屬於藍色,6屬於紅色,然後虛線的點表示還不在虛樹上的點,他們有一下幾種情況。。。
1.夾在了虛樹的一條邊上(a,b)(如2,3,4,5,7號節點)
假如a和b的歸屬點相同,那麽2,3,4,5,7號節點的歸屬點應該和a,b的顏色相同;
假如a和b的顏色不同,那麽路徑上必然存在一個分界點,使得一邊歸a管,一邊歸b管,那麽這個可以通過倍增來找到分界點。。。
2.沒有夾在虛樹的一條邊上(如8,9,10,11號節點)那麽這些節點的歸屬點必然就是這一片子樹的父親的歸屬點。。。
這樣看來嘴巴AC還是比較容易的,具體有一些實現的細節。。。
對於一條邊(a,b)(假設deep[a]<deep[b]),需要找到一個既是a的兒子又是b的祖先的一個點x,也就是a的兒子中子樹裏含b的點,也就是只考慮夾在路徑間的。。。
那麽如果a,b同色,那麽ans[bel[a]]+=size[x];
如果a,b不同色,我們通過倍增找到了路徑上的分界點mid,然後ans[bel[a]]+=size[x]-size[mid],ans[bel[b]]+=size[mid]-size[b];
然後考慮第二種情況,我們用res數組,res[i]表示i的子樹中還沒有歸屬點的點數(初始值為size[i]),那麽res數組在處理第一種情況的時候,即時res[i]-size[x]即可,最後再加上;
// MADE BY QT666 #include<cstdio> #include<algorithm> #include<cmath> #include<iostream> #include<cstring> using namespace std; typedef long long ll; const int N=1000050; struct data{ int head[N],to[N],nxt[N],v[N],cnt; void lnk(int x,int y){ if(x==y) return; to[++cnt]=y,nxt[cnt]=head[x],head[x]=cnt; to[++cnt]=x,nxt[cnt]=head[y],head[y]=cnt; } }g,g2; int size[N],dfn[N],tt,son[N],top[N],fa[N],deep[N],n,q,k; int a[N],b[N],zhan[N],bel[N],Father[N][20],ans[N],res[N],tot,xu[N]; bool cmp(int a,int b){return dfn[a]<dfn[b];} void dfs1(int x,int f){ size[x]=1;fa[x]=f;Father[x][0]=f; for(int i=1;i<=17;i++) Father[x][i]=Father[Father[x][i-1]][i-1]; for(int i=g.head[x];i;i=g.nxt[i]){ int y=g.to[i];if(y==f) continue; deep[y]=deep[x]+1;dfs1(y,x); size[x]+=size[y];if(size[y]>size[son[x]]) son[x]=y; } } void dfs2(int x,int f){ top[x]=f;dfn[x]=++tt; if(son[x]) dfs2(son[x],f); for(int i=g.head[x];i;i=g.nxt[i]){ int y=g.to[i];if(y==fa[x]||y==son[x]) continue; dfs2(y,y); } } int Lca(int x,int y){ while(top[x]!=top[y]){ if(deep[top[x]]<deep[top[y]]) swap(x,y); x=fa[top[x]]; } if(deep[x]<deep[y]) swap(x,y); return y; } int calc(int x,int y){return deep[x]+deep[y]-2*deep[Lca(x,y)];} void DFS1(int x,int f){ res[x]=size[x];xu[++tot]=x; for(int i=g2.head[x];i;i=g2.nxt[i]){ int y=g2.to[i];if(y==f) continue; DFS1(y,x); if(bel[y]){ if(!bel[x]) bel[x]=bel[y]; else{ int d1=calc(x,bel[x]),d2=calc(x,bel[y]); if(d2<d1||(d2==d1&&bel[y]<bel[x])) bel[x]=bel[y]; } } } } void DFS2(int x,int f){ for(int i=g2.head[x];i;i=g2.nxt[i]){ int y=g2.to[i];if(y==f) continue; if(bel[x]){ if(!bel[y]) bel[y]=bel[x]; else{ int d1=calc(y,bel[y]),d2=calc(y,bel[x]); if(d2<d1||(d2==d1&&bel[x]<bel[y])) bel[y]=bel[x]; } } DFS2(y,x); } } void build(){ scanf("%d",&k);tot=0; for(int i=1;i<=k;i++) scanf("%d",&a[i]),bel[a[i]]=a[i],b[i]=a[i]; sort(a+1,a+1+k,cmp);int tp=0;g2.cnt=1;if(!bel[1]) zhan[++tp]=1; for(int i=1;i<=k;i++){ if(tp==0){zhan[++tp]=a[i];continue;} int p=Lca(zhan[tp],a[i]); while(1){ if(deep[zhan[tp-1]]<=deep[p]){ g2.lnk(zhan[tp],p);tp--; if(zhan[tp]!=p) zhan[++tp]=p; break; } g2.lnk(zhan[tp],zhan[tp-1]);tp--; } zhan[++tp]=a[i]; } while(tp>1) g2.lnk(zhan[tp],zhan[tp-1]),tp--; DFS1(zhan[tp],zhan[tp]);DFS2(zhan[tp],zhan[tp]); } void solve(int x,int y){ int g=y;if(deep[x]>deep[y]) swap(x,y); for(int i=17;i>=0;i--){ if(Father[g][i]&&deep[Father[g][i]]>deep[x]) g=Father[g][i]; } res[x]-=size[g]; if(bel[x]==bel[y]){ans[bel[x]]+=size[g]-size[y];return;} int mid=y; for(int i=17;i>=0;i--){ int Mid=Father[mid][i]; int d1=calc(bel[x],Mid),d2=calc(bel[y],Mid); if(!Mid) continue; if((d2<d1||(d2==d1&&bel[y]<bel[x]))&&deep[Mid]>=deep[g]) mid=Mid; } ans[bel[x]]+=size[g]-size[mid];ans[bel[y]]+=size[mid]-size[y]; } int main(){ scanf("%d",&n); for(int i=1;i<n;i++){int x,y;scanf("%d%d",&x,&y);g.lnk(x,y);} dfs1(1,0);dfs2(1,1);scanf("%d",&q); while(q--){ build(); for(int i=2;i<=g2.cnt;i+=2) solve(g2.to[i],g2.to[i^1]); for(int i=1;i<=tot;i++) ans[bel[xu[i]]]+=res[xu[i]]; for(int i=1;i<=k;i++) printf("%d ",ans[b[i]]),ans[b[i]]=0; puts("");for(int i=1;i<=tot;i++) g2.head[xu[i]]=0,bel[xu[i]]=0; } return 0; }
bzoj 3572: [Hnoi2014]世界樹