NKOJ 3941 (HNOI 2014)世界樹(虛樹+樹形dp+倍增)

P3941[Hnoi2014]世界樹

問題描述

世界樹是一棵無比巨大的樹,它伸出的枝幹構成了整個世界。在這裏,生存着各種各樣的種族和生靈,他們共同信奉着絕對公正公平的女神艾莉森,在他們的信條裏,公平是使世界樹能夠生生不息、持續運轉的根本基石。
世界樹的形態可以用一個數學模型來描述:世界樹中有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年的時間裏,每一年完成授權後,當年每個臨時議事處將會管理多少個種族(議事處所在的聚居地也將接受該議事處管理)。 現在這個任務交給了以智慧著稱的靈長類的你:程序猿。請幫國王完成這個任務吧。

輸入格式

第一行爲一個正整數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]],表示被授權爲臨時議事處的聚居地編號(保證互不相同)。

輸出格式

輸出包含q行,第i行爲m[i]個整數,該行的第j(j=1,2…,,m[i])個數表示第i年被授權的聚居地h[j]的臨時議事處管理的種族個數。

樣例輸入

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

樣例輸出

1 9
3 1 4 1 1
10
1 1 3 5
4 1 3 1 1

提示

N<=300000, q<=300000,m[1]+m[2]+…+m[q]<=300000


注意到多次詢問的點總數和n同階,考慮虛樹。
構建好虛樹後,在虛樹上做兩次樹dp,算出到每個點距離最近的關鍵點,然後統計答案。
注意到有些點不在虛樹中,但也要統計答案,這些點可以分成兩類,一類是在虛樹中兩點之間的點,一類是虛樹中點的(不在虛樹中的)子樹中的點。
對於第二類點,可以在虛樹上用原size減去在虛樹中的子樹的size,注意這裏的size需要倍增找到該點在原樹中的兒子。
對於第一類點,可以枚舉一條虛樹中的邊,然後在邊上倍增找到中點,然後將兩段的size分別加到兩端的最近點上。
總時間複雜度O(nlogn)


代碼:

#include<stdio.h>
#include<iostream>
#include<algorithm>
#include<cstring>
#define N 600005
using namespace std;
int n,q,P[N],ans[N],Q[N],top,cnt,PP[N];
int dfn[N],VT,F[N],G[N],Sum[N],Size[N];
bool mark[N];
int dep[N],fa[N][20],S=19;
int TOT,LA[N],NE[N],EN[N];
int tot,la[N],ne[N],st[N],en[N],le[N];
bool cmp(int x,int y)
{return dfn[x]<dfn[y];}
void ADD(int x,int y)
{
    TOT++;
    EN[TOT]=y;
    NE[TOT]=LA[x];
    LA[x]=TOT;
}
void add(int x,int y,int z)
{
    tot++;
    st[tot]=x;
    en[tot]=y;
    le[tot]=z;
    ne[tot]=la[x];
    la[x]=tot;
}
void DFS(int x,int f)
{
    int i,y;
    Size[x]=1;
    dfn[x]=++VT;
    fa[x][0]=f;
    dep[x]=dep[f]+1;
    for(i=1;i<=S;i++)fa[x][i]=fa[fa[x][i-1]][i-1];
    for(i=LA[x];i;i=NE[i])
    {
        y=EN[i];
        if(y!=f)DFS(y,x),Size[x]+=Size[y];
    }
}
int LCA(int x,int y)
{
    if(!x||!y)return 0;
    if(dep[x]<dep[y])swap(x,y);
    int i,t=dep[x]-dep[y];
    for(i=0;i<=S;i++)
    if(t>>i&1)x=fa[x][i];
    if(x==y)return x;
    for(i=S;i>=0;i--)
    if(fa[x][i]!=fa[y][i])x=fa[x][i],y=fa[y][i];
    return fa[x][0];
}
void BT()
{
    int i,lca;
    if(!mark[1])Q[++top]=1;
    for(i=1;i<=cnt;i++)
    {
        lca=LCA(P[i],Q[top]);
        if(lca==Q[top]){Q[++top]=P[i];continue;}
        while(dep[lca]<dep[Q[top-1]])
        {
            add(Q[top-1],Q[top],dep[Q[top]]-dep[Q[top-1]]);
            top--;
        }
        add(lca,Q[top],dep[Q[top]]-dep[lca]);
        if(lca!=Q[--top])Q[++top]=lca;
        Q[++top]=P[i];
    }
    while(--top)add(Q[top],Q[top+1],dep[Q[top+1]]-dep[Q[top]]);
}
int Gsi(int x,int f)
{
    int i;
    for(i=S;i>=0;i--)if(dep[fa[x][i]]>dep[f])x=fa[x][i];
    return Size[x];
}
void DP1(int x)
{
    int i,y;F[x]=G[x]=1e9;Sum[x]=Size[x];
    if(mark[x])F[x]=0,G[x]=x;
    for(i=la[x];i;i=ne[i])
    {
        y=en[i];DP1(y);Sum[x]-=Gsi(y,x);
        if(F[y]+le[i]<F[x])F[x]=F[y]+le[i],G[x]=G[y];
        else if(F[y]+le[i]==F[x]&&G[y]<G[x])G[x]=G[y];
    }
}
void DP3(int x,int d,int f)
{
    if(x!=1)
    {
        if(F[x]>F[f]+d)F[x]=F[f]+d,G[x]=G[f];
        else if(F[x]==F[f]+d&&G[x]>G[f])G[x]=G[f];
    }
    for(int i=la[x];i;i=ne[i])DP3(en[i],le[i],x);
}
void DP2(int x)
{
    ans[G[x]]+=Sum[x];
    for(int i=la[x];i;i=ne[i])DP2(en[i]);
    la[x]=mark[x]=0;
}
int Gans(int x,int t)
{
    int sum=0,i;
    for(i=0;i<=S;i++)
    if(t>>i&1)sum+=Size[fa[x][i]]-Size[x],x=fa[x][i];
    return sum;
}
int main_main()
{
    int i,j,k,x,y,d;
    scanf("%d",&n);
    for(i=1;i<n;i++)
    {
        scanf("%d%d",&x,&y);
        ADD(x,y);ADD(y,x);
    }
    DFS(1,0);
    scanf("%d",&q);
    for(i=1;i<=q;i++)
    {
        scanf("%d",&cnt);
        for(j=1;j<=cnt;j++)scanf("%d",&P[j]),mark[P[j]]=1,PP[j]=P[j];
        sort(P+1,P+cnt+1,cmp);tot=0;
        BT();DP1(1);DP3(1,0,0);DP2(1);
        for(j=1;j<=tot;j++)
        {
            if(le[j]==1)continue;
            x=st[j];y=en[j];
            if(dep[x]<dep[y])swap(x,y);
            if(G[x]==G[y])ans[G[x]]+=Gans(x,dep[x]-dep[y]-1);
            else
            {
                k=le[j]-1-(F[x]-F[y]);
                d=Gans(x,(k>>1)+(G[x]<G[y]?k&1:0));
                ans[G[x]]+=d;
                ans[G[y]]+=Gans(x,dep[x]-dep[y]-1)-d;
            }
        }
        for(j=1;j<=cnt;j++)printf("%d ",ans[PP[j]]),ans[PP[j]]=0;
        puts("");
    }
}
const int main_stack=16;   
char my_stack[128<<20];   
int main() {   
  __asm__("movl %%esp, (%%eax);\n"::"a"(my_stack):"memory");   
  __asm__("movl %%eax, %%esp;\n"::"a"(my_stack+sizeof(my_stack)-main_stack):"%esp");   
  main_main();   
  __asm__("movl (%%eax), %%esp;\n"::"a"(my_stack):"%esp");   
  return 0;   
}  
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章