CF1137F Matches Are Not a Child's Play 题解

题目链接

点击打开链接

题目解法

参考 abruce 的非 \(lct\) 的做法

\(compare\) 操作是搞笑的,可以转化成求 \(u,v\)\(when\) 操作

一个结论是编号最大的点一定是最晚删的,不妨令编号最大的点为根,则删除顺序一定是从下往上删的
先考虑原树上单个点 \(u\)\(when\) 怎么求
\(mx_u\)\(u\) 子树中编号最大的点,\(to_u\) 表示 \(u\) 子树中编号最大点的位置
则满足 \(mx_v<mx_u\)\(v\) 一定比 \(u\) 先选到
考虑 \(mx_v=mx_u\)\(v\) 有多少个比 \(u\) 先选
画个图不难发现,\(u\) 子树中只会剩下 \(u\)\(to_u\) 的一条链,那么从下往上删即可
所以单个点 \(u\)\(when\) 即为 \(\sum [mx_v<mx_u]+dist(u,to_u)\)

操作 \(up\) 相当于把根换成 \(u\)
考虑如何维护出新的 \(mx\)\(to\)
不难发现,只有原根到新根的路径上的点的 \(mx\) 会有影响,且 \(mx\) 都变成原根的编号(除了 \(mx_u=\max+1\)
而且更好的是,这些点的 \(to\) 都为原根(且换根时不会影响除了路径上的其他点的 \(to\)

这就好做了,我们现在只需要维护区间改颜色,和查颜色 \(<x\) 的数的个数(用树剖转化成区间操作)
这里有一个很常用的维护颜色段的 \(trick\):颜色段均摊
即用 \(set\) 维护每个颜色段,每次修改暴力删和加即可
因为加入的段是 \(O(n+q\log n)\) 的,而每个段只会删除一次,所以复杂度是 \(O(n\log n+q\log^2 n)\)
\(<x\) 的数的个数直接套个树状数组即可

时间复杂度 \(O(n\log n+q\log^2 n)\)

#include <bits/stdc++.h>
#define F(i,x,y) for(int i=(x);i<=(y);i++)
#define DF(i,x,y) for(int i=(x);i>=(y);i--)
#define ms(x,y) memset(x,y,sizeof(x))
#define SZ(x) (int)x.size()-1
#define all(x) x.begin(),x.end()
#define pb push_back
using namespace std;
typedef long long LL;
typedef unsigned long long ull;
typedef pair<int,int> pii;
template<typename T> void chkmax(T &x,T y){ x=max(x,y);}
template<typename T> void chkmin(T &x,T y){ x=min(x,y);}
template<typename T> void read(T &FF){
    FF=0;int RR=1;char ch=getchar();
    for(;!isdigit(ch);ch=getchar()) if(ch=='-') RR=-1;
    for(;isdigit(ch);ch=getchar()) FF=(FF<<1)+(FF<<3)+ch-48;
    FF*=RR;
}
const int N=400010;
vector<int> G[N];
int fa[N],siz[N],son[N],dep[N],top[N],dfn[N],rv[N],clk;
int mx[N];
int st[N][20],lg[N],cnt,fir[N];
void dfs1(int u){
    siz[u]=1,dep[u]=dep[fa[u]]+1,mx[u]=u;
    st[++cnt][0]=dep[u],fir[u]=cnt;
    for(int v:G[u]) if(v!=fa[u]){
        fa[v]=u,dfs1(v),st[++cnt][0]=dep[u];
        chkmax(mx[u],mx[v]);
        siz[u]+=siz[v];
        if(siz[v]>siz[son[u]]) son[u]=v;
    }
}
void dfs2(int u,int t){
    top[u]=t,dfn[u]=++clk,rv[clk]=u;
    if(!son[u]) return;
    dfs2(son[u],t);
    for(int v:G[u]) if(v!=fa[u]&&v!=son[u]) dfs2(v,v);
}
#define lowbit(x) x&-x
struct fenwick{
    int n,tr[N];
    void add(int x,int v){ for(;x<=n;x+=lowbit(x)) tr[x]+=v;}
    int ask(int x){ int res=0;for(;x;x-=lowbit(x)) res+=tr[x];return res;}
}tr;
struct ds{
    int l,r,v;
    bool operator <(const ds &o)const{ return l<o.l;}
};
set<ds> se;
int key[N];
auto split(int p){
    auto it=se.lower_bound({p,-1,-1});
    if(it!=se.end()&&it->l==p) return it;
    it--;
    if(it->r<p) return se.end();
    auto [l,r,v]=*it;
    se.erase(it),se.insert({l,p-1,v});
    return se.insert({p,r,v}).first;
}
void assign(int l,int r,int v){
    auto itr=split(r+1),itl=split(l);
    for(auto it=itl;it!=itr;it++) tr.add(it->v,-(it->r-it->l+1));
    se.erase(itl,itr);
    se.insert({l,r,v}),tr.add(v,r-l+1);
}
void mdf_sgl(int u,int submx){ assign(dfn[u],dfn[u],submx);}
void mdf_path(int u,int v,int submx){
    while(top[u]!=top[v]){
        if(dep[top[u]]<dep[top[v]]) swap(u,v);
        assign(dfn[top[u]],dfn[u],submx),u=fa[top[u]];
    }
    if(dfn[u]>dfn[v]) swap(u,v);
    assign(dfn[u],dfn[v],submx);
}
int lcadep(int x,int y){
    x=fir[x],y=fir[y];
    if(x>y) swap(x,y);
    int k=lg[y-x+1];
    return min(st[x][k],st[y-(1<<k)+1][k]);
}
int dist(int x,int y){ return dep[x]+dep[y]-lcadep(x,y)*2+1;}
int query(int u){
    auto it=se.lower_bound({dfn[u]+1,-1,-1});it=prev(it);
    return tr.ask(it->v-1)+dist(u,key[it->v]);
}
int main(){
    int n,q;read(n),read(q);tr.n=n+q;
    F(i,1,n-1){
        int x,y;read(x),read(y);
        G[x].pb(y),G[y].pb(x);
    }
    dfs1(n),dfs2(n,n);
    F(i,2,cnt) lg[i]=lg[i>>1]+1;
    F(j,1,18) F(i,1,cnt-(1<<j)+1) st[i][j]=min(st[i][j-1],st[i+(1<<(j-1))][j-1]);
    int rt=n;
    F(i,1,n) key[i]=i;
    F(i,1,n) tr.add(mx[i],1),se.insert({dfn[i],dfn[i],mx[i]});
    while(q--){
        char op[10];scanf("%s",op);
        int u,v;read(u);
        if(op[0]=='u'){
            mdf_path(rt,u,n);
            key[++n]=u,mdf_sgl(u,n),rt=u;
        }
        else if(op[0]=='w') printf("%d\n",query(u));
        else{
            read(v);
            if(query(u)<query(v)) printf("%d\n",u);else printf("%d\n",v);
        }
    }
    return 0;
}

posted @ 2024-04-22 19:43  Farmer_D  阅读(10)  评论(0)    收藏  举报