[HNOI2014]世界樹
Description
世界樹是一棵無比巨大的樹,它伸出的枝幹構成了整個世界。在這裏,生存著各種各樣的種族和生靈,他們共同信奉著絕對公正公平的女神艾莉森,在他們的信條裏,公平是使世界樹能夠生生不息、持續運轉的根本基石。
世界樹的形態可以用一個數學模型來描述:世界樹中有n個種族,種族的編號分別從1到n,分別生活在編號為1到n的聚居地上,種族的編號與其聚居地的編號相同。有的聚居地之間有雙向的道路相連,道路的長度為1。保證連接的方式會形成一棵樹結構,即所有的聚居地之間可以互相到達,並且不會出現環。定義兩個聚居地之間的距離為連接他們的道路的長度;例如,若聚居地a和b之間有道路,b和c之間有道路,因為每條道路長度為1而且又不可能出現環,所臥a與c之間的距離為2。
現在國王想知道,在q年的時間裏,每一年完成授權後,當年每個臨時議事處將會管理多少個種族(議事處所在的聚居地也將接受該議事處管理)。 現在這個任務交給了以智慧著稱的靈長類的你:程序猿。請幫國王完成這個任務吧。
Input
第一行為一個正整數n,表示世界樹中種族的個數。
接下來n-l行,每行兩個正整數x,y,表示x聚居地與y聚居地之間有一條長度為1的雙
接下來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
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]世界樹