您的位置:首页 > 其它

【codeforces685B686D】【Kay and Snowflake】【线段树合并】

2016-06-26 20:28 288 查看

题目大意

给定30万个点的树,要求支持30万个询问,询问以某个点为根的子树的重心。

题解

给每的节点建立权值线段树,记录每个子节点为根的子树大小。重心为根的子树大小最接近但大于全树大小的一半。使用线段树合并即可解决本问题。

code

#include<set>
#include<cmath>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define fo(i,j,k) for(int i=j;i<=k;i++)
#define fd(i,j,k) for(int i=j;i>=k;i--)
using namespace std;
int const maxn=300000;
int cntgra,cntpon,n,q,to[maxn+10],next[maxn+10],begin[maxn+10],tsize[maxn*18*2+10],son[maxn*18*2+10][2],
pon[maxn*18*2+10],ans[maxn+10],size[maxn+10];
void insert(int u,int v){
to[++cntgra]=v;
next[cntgra]=begin[u];
begin[u]=cntgra;
}
int merge(int u,int v){
if(!tsize[u])return v;
if(!tsize[v])return u;
son[u][0]=merge(son[u][0],son[v][0]);
son[u][1]=merge(son[u][1],son[v][1]);
tsize[u]=tsize[son[u][0]]+tsize[son[u][1]];
return u;
}
int ask(int now,int l,int r,int tg){
int m=(l+r)/2;
if(!tsize[now])return 0;
if(l==r)return pon[now];
if(tg<=m){
int tmp=ask(son[now][0],l,m,tg);
if(tmp)return tmp;
else return ask(son[now][1],m+1,r,tg);
}
else return ask(son[now][1],m+1,r,tg);
}
void change(int now,int l,int r,int tg,int v){
int m=(l+r)/2;
tsize[now]++;
if(l==r)pon[now]=v;
else if(tg<=m){
if(!son[now][0])son[now][0]=++cntpon;
change(son[now][0],l,m,tg,v);
}
else{
if(!son[now][1])son[now][1]=++cntpon;
change(son[now][1],m+1,r,tg,v);
}
}
void dfs(int now){
size[now]=1;
for(int i=begin[now];i!=-1;i=next[i]){
dfs(to[i]);
size[now]+=size[to[i]];
son[now][0]=merge(son[now][0],son[to[i]][0]);
son[now][1]=merge(son[now][1],son[to[i]][1]);
}
change(now,1,n,size[now],now);
ans[now]=ask(now,1,n,(size[now]+1)/2);
}
int main(){
scanf("%d%d",&n,&q);
memset(begin,255,sizeof(begin));cntpon=n;
fo(i,2,n){
int x;scanf("%d",&x);
insert(x,i);
}
dfs(1);
fo(i,1,q){
int x;scanf("%d",&x);
printf("%d\n",ans[x]);
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: