[洛谷P4299] 首都

题目传送门

还是维护子树信息。

但是这里多了一个找重心的操作。

这里有一个关于树重心的结论,据说可以用反证法证明。反正我不会证

就是:新的重心一定在原来两个重心之间的那条树链上。

这样我们逐步缩小搜索范围,就可以很快地找到新树的重心了。

另外,每次找重心太慢了,用并查集维护一下每个点所在的树重心。

其他的就没什么特别了。

  1 #include<cstdio>
  2 #include<cstring>
  3 #include<algorithm>
  4 #define id(x) (s[f[x]][1]==x)
  5 using namespace std;
  6 
  7 int n,m;
  8 int s[100005][2],f[100005];
  9 int sz[100005],szi[100005];
 10 int rev[100005],rt[100005];
 11 int ff[100005];
 12 
 13 int findfa(int x)
 14 {
 15     if(x==ff[x])return x;
 16     ff[x]=findfa(ff[x]);
 17     return ff[x];
 18 }
 19 
 20 void pushup(int p)
 21 {
 22     sz[p]=sz[s[p][0]]+sz[s[p][1]]+szi[p]+1;
 23 }
 24 
 25 void reverse(int p)
 26 {
 27     swap(s[p][0],s[p][1]);
 28     rev[p]^=1;
 29 }
 30 
 31 void pushdown(int p)
 32 {
 33     if(!rev[p])return;
 34     reverse(s[p][0]);
 35     reverse(s[p][1]);
 36     rev[p]=0;
 37 }
 38 
 39 void rotate(int p)
 40 {
 41     int k=id(p);
 42     int fa=f[p];
 43     if(rt[fa])rt[p]=1,rt[fa]=0;
 44     else s[f[fa]][id(fa)]=p;
 45     s[fa][k]=s[p][!k];
 46     s[p][!k]=fa;
 47     f[p]=f[fa];
 48     f[fa]=p;
 49     f[s[fa][k]]=fa;
 50     pushup(fa);
 51     pushup(p);
 52 }
 53 
 54 void down(int p)
 55 {
 56     if(!rt[p])down(f[p]);
 57     pushdown(p);
 58 }
 59 
 60 void splay(int p)
 61 {
 62     down(p);
 63     while(!rt[p])
 64     {
 65         int fa=f[p];
 66         if(rt[fa])
 67         {
 68             rotate(p);
 69             return;
 70         }
 71         if(id(p)^id(fa))rotate(p);
 72         else rotate(fa);
 73         rotate(p);
 74     }
 75 }
 76 
 77 void access(int p)
 78 {
 79     int son=0;
 80     while(p)
 81     {
 82         splay(p);
 83         szi[p]+=sz[s[p][1]];
 84         rt[s[p][1]]=1,rt[son]=0;
 85         s[p][1]=son;
 86         szi[p]-=sz[s[p][1]];
 87         pushup(p);
 88         son=p,p=f[p];
 89     }
 90 }
 91 
 92 void mtr(int p)
 93 {
 94     access(p);
 95     splay(p);
 96     reverse(p);
 97 }
 98 
 99 void isolate(int x,int y)
100 {
101     mtr(x);
102     access(y);
103     splay(y);
104 }
105 
106 int nueva(int p)
107 {
108     int l,r,sum=sz[p]>>1,tot=sz[p]&1,lsum=0,rsum=0,ng=n+1,nl,nr;
109     while(p)
110     {
111         pushdown(p);
112         l=s[p][0],r=s[p][1];
113         nl=sz[l]+lsum,nr=sz[r]+rsum;
114         if(nl<=sum&&nr<=sum)
115         {
116             if(tot)
117             {
118                 ng=p;
119                 break;
120             }else
121             if(ng>p)ng=p;
122         }
123         if(nl<nr)lsum+=sz[l]+szi[p]+1,p=r;
124         else rsum+=sz[r]+szi[p]+1,p=l;
125     }
126     splay(ng);
127     return ng;
128 }
129 
130 int xsum;
131 
132 int main()
133 {
134     scanf("%d%d",&n,&m);
135     for(int i=1;i<=n;i++)
136     {
137         sz[i]=rt[i]=1;
138         ff[i]=i;
139         xsum^=i;
140     }
141     for(int i=1;i<=m;i++)
142     {
143         char op[5];
144         scanf("%s",op+1);
145         if(op[1]=='A')
146         {
147             int x,y;
148             scanf("%d%d",&x,&y);
149             isolate(x,y);
150             f[x]=y;
151             szi[y]+=sz[x];
152             pushup(y);
153             x=findfa(x),y=findfa(y);
154             isolate(x,y);
155             int ng=nueva(y);
156             xsum=xsum^x^y^ng;
157             ff[x]=ff[y]=ff[ng]=ng;
158         }
159         if(op[1]=='Q')
160         {
161             int x;
162             scanf("%d",&x);
163             printf("%d\n",findfa(x));
164         }
165         if(op[1]=='X')printf("%d\n",xsum);
166     }
167     return 0;
168 }

 

转载于:https://www.cnblogs.com/eternhope/p/9674664.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值