CF1137F Matches Are Not a Child's Play
一、题目
二、解法
思维含量和代码难度都点满了,但是我喜欢写这种码农题(除了插头 \(dp\))😅
问题很简单,就是每次把某个节点编号变为最大的情况下维护出这个删除序列。那么我们考虑这个操作有什么特别的性质,就让小编来带你们看看吧!
无根树问题可以优先考虑定根,本题可以考虑设置编号最大的点为根,那么根一定是最后删除的。假如此时我们 up u
,那么 \(u\) 到根路径的点会成为最后一个、倒数第二个、倒数第三个\(...\)也就是我们把路径上的点取出来移动到序列末尾,然后其它点的删除顺序不变。
修改某个点到根路径可以考虑 \(\tt lct\),而我们此时可以套用染色模型,也就是修改看成某个点到根的染色,然后实边代表颜色相同,虚边代表颜色不同。那么初始的树可以依次按编号从大到小染色,遇到已经被染色的节点就停止,对于修改 up v
相当于用一种最大的新颜色处理它到根的路径。
那么我们考虑如何通过染色来得到一个点在删除序列中的位置呢?发现就是颜色比它小的点个数 和 与它同色并且和深度比它大的点个数。第一部分可以用树状数组维护,在 \(\tt access\) 的时候在上面修改一个颜色对应的个数即可,第二部分就是在它所在的同色链中计算即可。
时间复杂度 \(O(n\log n)\),常数因为树状数组特别地小。
#include <cstdio>
#include <iostream>
using namespace std;
const int M = 200005;
int read()
{
int x=0,f=1;char c;
while((c=getchar())<'0' || c>'9') {if(c=='-') f=-1;}
while(c>='0' && c<='9') {x=(x<<3)+(x<<1)+(c^48);c=getchar();}
return x*f;
}
int n,m,k,UP,tot,f[M],b[M<<1];char zxy[10];
int st[M],ch[M][2],siz[M],fa[M],col[M],cv[M],fl[M];
struct edge
{
int v,next;
}e[M<<1];
//tree-array
int lowbit(int x)
{
return x&(-x);
}
void add(int x,int c)
{
for(int i=x;i<=UP;i+=lowbit(i))
b[i]+=c;
}
int ask(int x)
{
int r=0;
for(int i=x;i>=1;i-=lowbit(i))
r+=b[i];
return r;
}
//link-cut-tree
int nrt(int x)
{
return ch[fa[x]][0]==x || ch[fa[x]][1]==x;
}
int chk(int x)
{
return ch[fa[x]][1]==x;
}
void up(int x)
{
siz[x]=siz[ch[x][0]]+siz[ch[x][1]]+1;
}
void flip(int x)
{
if(!x) return ;
swap(ch[x][0],ch[x][1]);fl[x]^=1;
}
void cover(int x,int c)
{
if(!x) return ;
col[x]=cv[x]=c;
}
void down(int x)
{
if(fl[x])
flip(ch[x][0]),flip(ch[x][1]),fl[x]=0;
if(cv[x])
cover(ch[x][0],cv[x]),cover(ch[x][1],cv[x]),cv[x]=0;
}
void rotate(int x)
{
int y=fa[x],z=fa[y],k=chk(x),w=ch[x][k^1];
ch[y][k]=w;fa[w]=y;
if(nrt(y)) ch[z][chk(y)]=x;fa[x]=z;
ch[x][k^1]=y;fa[y]=x;
up(y);up(x);
}
void splay(int x)
{
int y=x,k=0;st[k=1]=x;
while(nrt(y)) y=fa[y],st[++k]=y;
while(k) down(st[k--]);
while(nrt(x))
{
int y=fa[x];
if(nrt(y))
{
if(chk(x)==chk(y)) rotate(y);
else rotate(x);
}
rotate(x);
}
}
void access(int x)//modified version
{
int u=x;++k;
for(int y=0;x;x=fa[y=x])
{
splay(x);ch[x][1]=0;up(x);
add(col[x],-siz[x]);ch[x][1]=y;up(x);
}
splay(u);cover(u,k);flip(u);
add(col[u],siz[u]);
}
int qry(int x)
{
splay(x);
return ask(col[x]-1)+siz[ch[x][1]]+1;
}
//dfs & initialize
void dfs(int u,int p)
{
fa[u]=p;
for(int i=f[u];i;i=e[i].next)
{
int v=e[i].v;
if(v==p) continue;
dfs(v,u);
}
}
signed main()
{
n=read();m=read();k=n;UP=n+m;
for(int i=1;i<=n;i++) siz[i]=1;
for(int i=1;i<n;i++)
{
int u=read(),v=read();
e[++tot]=edge{v,f[u]},f[u]=tot;
e[++tot]=edge{u,f[v]},f[v]=tot;
}
dfs(n,0);
for(int i=n,nw=n;i>=1;i--)
{
if(col[i]) continue;
int u=i,cnt=0,v=0;
while(!col[u] && u)
col[u]=nw,ch[u][1]=v,up(u),
v=u,u=fa[u],cnt++;
add(nw,cnt);nw--;
}
while(m--)
{
scanf("%s",zxy);
if(zxy[0]=='u')
access(read());
if(zxy[0]=='w')
printf("%d\n",qry(read()));
if(zxy[0]=='c')
{
int u=read(),v=read();
if(qry(u)<qry(v)) printf("%d\n",u);
else printf("%d\n",v);
}
}
}