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

[HNOI2014]世界樹

strong turn type 世界 如何 nal wol join 斷點

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

為了這題特意學了倍增來著……

首先肯定建出虛樹,然後考慮如何統計答案。首先我們可以通過兩次dfs,對虛樹上每個點記錄最近的標記點,並且求出距離(pair的妙用)

然後我們考慮虛樹上的每條邊,如果兩端點記錄的標記點相同,則這兩個點在原樹上所屬的子樹都給該標記點貢獻了答案,註意之前算過的子樹不能重復計算

如果兩端點記錄的標記點不同,那麽代表必然在邊上存在一個原樹的點,將邊分成兩個部分,我們可以通過兩個端點記錄的距離,以及邊的長度等求出斷點的位置,然後倍增找到斷點,分兩部分統計答案即可

/*program from Wolfycz*/
#include<cmath>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define Fi first
#define Se second
#define MK make_pair
#define inf 0x7f7f7f7f
#define lowbit(x) ((x)&-(x))
using namespace std;
typedef long long ll;
typedef unsigned int ui;
typedef pair<int,int> pii;
typedef unsigned long long ull;
inline char gc(){
    static char buf[1000000],*p1=buf,*p2=buf;
    return p1==p2&&(p2=(p1=buf)+fread(buf,1,1000000,stdin),p1==p2)?EOF:*p1++;
}
inline int frd(){
    int x=0,f=1; char ch=gc();
    for (;ch<'0'||ch>'9';ch=gc())   if (ch=='-')    f=-1;
    for (;ch>='0'&&ch<='9';ch=gc()) x=(x<<3)+(x<<1)+ch-'0';
    return x*f;
}
inline int read(){
    int x=0,f=1; char ch=getchar();
    for (;ch<'0'||ch>'9';ch=getchar())  if (ch=='-')    f=-1;
    for (;ch>='0'&&ch<='9';ch=getchar())    x=(x<<3)+(x<<1)+ch-'0';
    return x*f;
}
inline void print(int x){
    if (x<0)    putchar('-'),x=-x;
    if (x>9)    print(x/10);
    putchar(x%10+'0');
}
const int N=3e5;
int dfn[N+10],Lg[(1<<19)+10],root;
pii operator +(pii a,int b){return MK(a.Fi+b,a.Se);}
bool cmp(int x,int y){return dfn[x]<dfn[y];}
struct S1{
    int pre[(N<<1)+10],now[N+10],child[(N<<1)+10],val[(N<<1)+10];
    int deep[N+10],size[N+10],f[20][N+10],tot,Time;
    void join(int x,int y){pre[++tot]=now[x],now[x]=tot,child[tot]=y;}
    void insert(int x,int y){join(x,y),join(y,x);}
    void dfs(int x,int fa){
        dfn[x]=++Time,size[x]=1;
        deep[x]=deep[f[0][x]=fa]+1;
        for (int i=1;i<20;i++)  f[i][x]=f[i-1][f[i-1][x]];
        for (int p=now[x],son=child[p];p;p=pre[p],son=child[p]){
            if (son==fa)    continue;
            dfs(son,x);
            size[x]+=size[son];
        }
    }
    int dis(int x,int y){return abs(deep[x]-deep[y]);}
    int jump(int x,int v){
        if (v<0)    return 0;
        for (;v;v-=lowbit(v))   x=f[Lg[lowbit(v)]][x];
        return x;
    }
    int LCA(int x,int y){
        if (deep[x]<deep[y])    swap(x,y);
        x=jump(x,deep[x]-deep[y]);
        if (x==y)   return x;
        for (int i=19;~i;i--)   if (f[i][x]!=f[i][y])   x=f[i][x],y=f[i][y];
        return f[0][x];
    }
}OT;//Original Tree
struct S2{
    int pre[(N<<1)+10],now[N+10],child[(N<<1)+10],val[(N<<1)+10],tot;
    int A[N+10],size[N+10],ID[N+10],stack[N+10],Ans[N+10];
    pii f[N+10];//control
    void join(int x,int y,int z){pre[++tot]=now[x],now[x]=tot,child[tot]=y,val[tot]=z;}
    void insert(int x,int y,int z=0){z=OT.dis(x,y),join(x,y,z),join(y,x,z);}
    void rebuild(int m){
        int top=0; tot=0;
        sort(A+1,A+1+m,cmp);
        stack[++top]=root;
        for (int i=1;i<=m;i++){
            int x=A[i],lca=OT.LCA(x,stack[top]);
            if (lca==stack[top]){
                stack[++top]=x;
                continue;
            }
            while (true){
                int y=stack[top-1];
                if (dfn[y]>=dfn[lca]){
                    insert(y,stack[top--]);
                    continue;
                }else{
                    if (lca==stack[top])    break;
                    insert(lca,stack[top]);
                    stack[top]=lca; break;
                }
            }
            stack[++top]=x;
        }
        while (top>1){
            insert(stack[top],stack[top-1]);
            top--;
        }
    }
    void dfs1(int x,int fa){
        f[x]=ID[x]?MK(0,x):MK(inf,0);
        size[x]=OT.size[x];
        for (int p=now[x],son=child[p];p;p=pre[p],son=child[p]){
            if (son==fa)    continue;
            dfs1(son,x);
            f[x]=min(f[x],f[son]+val[p]);
            size[x]-=OT.size[OT.jump(son,val[p]-1)];//不重復考慮
        }
    }
    void dfs2(int x,int fa){
        for (int p=now[x],son=child[p];p;p=pre[p],son=child[p]){
            if (son==fa)    continue;
            f[son]=min(f[son],f[x]+val[p]);
            dfs2(son,x);
        }
        for (int p=now[x],son=child[p];p;p=pre[p],son=child[p]){
            if (son==fa)    continue;
            //尋找斷點
            int V=f[x].Fi-f[son].Fi+val[p]-1;
            int res1=OT.size[OT.jump(son,val[p]-1)]+size[son]-OT.size[son];
            int res2=OT.size[OT.jump(son,V/2+((V>0)&&(V&1)&&(f[son].Se<f[x].Se)))]+size[son]-OT.size[son];
            res2=max(res2,0),res1-=res2;
            Ans[ID[f[ x ].Se]]+=res1;
            Ans[ID[f[son].Se]]+=res2;
        }
        now[x]=0;
    }
    void Debug(int x,int fa){
        printf("%d->%d %d\n",x,f[x].Fi,f[x].Se);
        for (int p=now[x],son=child[p];p;p=pre[p],son=child[p]){
            if (son==fa)    continue;
            Debug(son,x);
        }
        now[x]=0;
    }
    void work(){
        int m=read();
        for (int i=1;i<=m;i++)  ID[A[i]=read()]=i;
        rebuild(m),dfs1(root,0),dfs2(root,0);
        for (int i=1;i<=m;i++)  printf("%d",Ans[i]),putchar(i==m?'\n':' ');
        for (int i=1;i<=m;i++)  ID[A[i]]=0,Ans[i]=0;
    }
}VT;//Virtual Tree
int main(){
    for (int i=2;Lg[i>>1]<19;i<<=1) Lg[i]=Lg[i>>1]+1;
    int n=read(); root=n+1;
    for (int i=1;i<n;i++){
        int x=read(),y=read();
        OT.insert(x,y);
    }
    OT.insert(root,1);
    OT.dfs(root,0);
    int m=read();
    for (int i=1;i<=m;i++)  VT.work();
    return 0;
}

[HNOI2014]世界樹