1. 程式人生 > >bzoj 3572: [Hnoi2014]世界樹

bzoj 3572: [Hnoi2014]世界樹

png 樹結構 多少 方式 alt [0 truct int 藍色

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

10
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 9
3 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]世界樹