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

来源:互联网 发布:淘宝买处方药流程 编辑:程序博客网 时间:2024/06/05 14:46

题目大意

给定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;}
0 0