CF1137F 题解
先把无根树转化为有根树,可以令编号最大的点为根——它必定最后被删除。
之后考虑一次查询过程,查询
这已经可以处理比较操作了,但为了解决查询操作需要找出所有
令
但是我们还需要快速找出这些节点。注意到全程一个
之后找出
现在尝试用莫队维护一个特殊点组成的集合,需要在维护的集合中插入或者删除一个点。求 LCA 可以做到单次
利用这个题的技巧,使用只删除的回滚莫队,这样维护一个按照 dfs 序排好序的双向链表,就能直接查询前驱后继。
综上,假设
#include<bits/stdc++.h>
using namespace std;
#define ll int
#define lson (u<<1)
#define rson (u<<1|1)
const ll N=200007,B=700;
int n,m,k,nQ,w[N],a[N<<1];
int p[N],dep[N],id[N],tI[N],tO[N],timer,c[N];
int type[N],ans[N],iB[N<<1];
int pre[N],nxt[N],cnt[N],L,R,now;
vector<int> to[N];
char op[10];
struct query{
int u,l,r,id;
}qry[N<<1];
bool up(int u,int v){
return tI[u]<=tI[v]&&tO[v]<=tO[u];
}
namespace SGT{
int pos[N<<2];
int cmp(int x,int y){return w[x]>w[y]?x:y;}
void build(int u,int l,int r){
if (l==r){
pos[u]=id[l];
return;
}
int mid=l+r>>1;
build(lson,l,mid);
build(rson,mid+1,r);
pos[u]=cmp(pos[lson],pos[rson]);
}
void modify(int u,int l,int r,int x){
if (l==r){
return;
}
int mid=l+r>>1;
if (x<=mid){
modify(lson,l,mid,x);
}
else{
modify(rson,mid+1,r,x);
}
pos[u]=cmp(pos[lson],pos[rson]);
}
int query(int u,int l,int r,int L,int R){
if (L<=l&&r<=R){
return pos[u];
}
int mid=l+r>>1;
if (R<=mid){
return query(lson,l,mid,L,R);
}
if (L>mid){
return query(rson,mid+1,r,L,R);
}
return cmp(query(lson,l,mid,L,R),query(rson,mid+1,r,L,R));
}
}
namespace ST{
int mn[20][N],st[20][N];
int cmp(int x,int y){
return dep[x]<dep[y]?x:y;
}
void build(){
for (int i=1;i<=n;++i){
mn[0][i]=id[i];
st[0][i]=dep[id[i]];
}
for (int p=1;p<20;++p){
for (int i=1;i<=n-(1<<p)+1;++i){
mn[p][i]=cmp(mn[p-1][i],mn[p-1][i+(1<<p-1)]);
st[p][i]=min(st[p-1][i],st[p-1][i+(1<<p-1)]);
}
}
}
int query(int l,int r){
int k=__lg(r-l+1);
return cmp(mn[k][l],mn[k][r-(1<<k)+1]);
}
int query_dep(int l,int r){
int k=__lg(r-l+1);
return min(st[k][l],st[k][r-(1<<k)+1]);
}
}
int LCA(int u,int v){
if (u==v){
return u;
}
if (tI[u]>tI[v]){
return p[ST::query(tI[v]+1,tI[u])];
}
return p[ST::query(tI[u]+1,tI[v])];
}
int get_max_value(int root,int u){
if (up(u,root)){
u=ST::query(tI[u]+1,tI[root]);
if (tO[u]==n){
return SGT::query(1,1,n,1,tI[u]-1);
}
return SGT::cmp(SGT::query(1,1,n,1,tI[u]-1),SGT::query(1,1,n,tO[u]+1,n));
}
return SGT::query(1,1,n,tI[u],tO[u]);
}
int dis(int u,int v){
return dep[u]+dep[v]-2*dep[LCA(u,v)];
}
void dfs(int u,int fa){
p[u]=fa;dep[u]=dep[fa]+1;tI[u]=++timer;id[timer]=u;
for (auto v:to[u]){
if (v==fa){
continue;
}
dfs(v,u);
}
tO[u]=timer;
}
namespace SGT2{
ll val[N<<3];
void build(int u,int l,int r){
if (l==r){
val[u]=dep[LCA(a[l-1],a[l])]-1;
return;
}
int mid=l+r>>1;
build(lson,l,mid);build(rson,mid+1,r);
val[u]=min(val[lson],val[rson]);
}
int query(int u,int l,int r,int L,int R){
if (L<=l&&r<=R){
return val[u];
}
int mid=l+r>>1;
if (R<=mid){
return query(lson,l,mid,L,R);
}
if (L>mid){
return query(rson,mid+1,r,L,R);
}
return min(query(lson,l,mid,L,R),query(rson,mid+1,r,L,R));
}
}
int main(){
ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
cin>>n>>m;
k=n;
for (int u,v,i=1;i<n;++i){
cin>>u>>v;
to[u].emplace_back(v);to[v].emplace_back(u);
}
dfs(1,0);
ST::build();
for (int i=1;i<=n;++i){
w[i]=a[i]=i;c[i]=1;
}
SGT::build(1,1,n);
auto add_query=[&](int u,int id){
if (u==a[k]){
ans[id]=n;
return;
}
int v=get_max_value(a[k],u);
qry[++nQ]=(query){u,w[v],k,id};
ans[id]=n+dis(u,v)+1;
};
for (int u,v,i=1;i<=m;++i){
cin>>op;
type[i]=op[0];
if (op[0]=='u'){
cin>>u;
a[++k]=u;++c[u];
w[u]=k;
SGT::modify(1,1,n,tI[u]);
}
else if (op[0]=='w'){
cin>>u;
add_query(u,i);
}
else{
cin>>u>>v;
if (u==a[k]||v==a[k]){
ans[i]=u^v^a[k];
}
else{
int U=get_max_value(a[k],u),V=get_max_value(a[k],v);
if (U!=V){
ans[i]=(w[U]<w[V]?u:v);
}
else{
ans[i]=(dis(u,U)<dis(v,V)?u:v);
}
}
}
}
for (int l=1,r,o=0;l<=k;l=r+1){
r=min(k,l+B-1);
++o;
for (int i=l;i<=r;++i){
iB[i]=o;
}
}
sort(qry+1,qry+1+nQ,[&](const query& a,const query& b){
return iB[a.l]==iB[b.l]?a.r>b.r:a.l<b.l;
});
auto del=[&](const int& x){
if (--cnt[x]){
return;
}
int l=pre[x],r=nxt[x];
nxt[l]=r;pre[r]=l;
now-=dep[id[x]]+ST::query_dep(l+1,r)-ST::query_dep(l+1,x)-ST::query_dep(x+1,r)+1;
};
auto add=[&](const int& x){
if ((++cnt[x])==1){
nxt[pre[x]]=pre[nxt[x]]=x;
}
};
now=n;L=1;R=k;
int A=n;
for (int i=1;i<=n;++i){
cnt[i]=c[id[i]];
pre[i]=i-1;nxt[i]=i+1;
}
SGT2::build(1,2,k);
for (int t=1;t<=nQ;++t){
if (iB[qry[t].l]!=iB[qry[t-1].l]){
// cerr<<iB[qry[t].l]<<' '<<t<<endl;
now=A;
while(R<k){
add(tI[a[++R]]);
}
while(iB[L]!=iB[qry[t].l]){
del(tI[a[L++]]);
}
A=now;
}
while(R>qry[t].r){
del(tI[a[R--]]);
}
int tnow=now,tl=L;
while(L<qry[t].l){
del(tI[a[L++]]);
}
ans[qry[t].id]-=now-SGT2::query(1,2,k,qry[t].l+1,qry[t].r);
while(L>tl){
add(tI[a[--L]]);
}
now=tnow;
}
for (int i=1;i<=m;++i){
if (type[i]=='w'||type[i]=='c'){
cout<<ans[i]<<'\n';
}
}
return 0;
}