题解 洛谷 P5385 【[Cnoi2019]须臾幻境】

首先我们知道 (n) 个点的树有 (n-1) 条边,因此对于森林来说,其点数减边数即为树的个数。那么对于普通的图,求出其任意一个生成树森林,森林中树的个数即为原图中连通块的个数,也就是点数减边数。

因此问题就转化为了如何快速求出一个图的生成树森林的边数。

考虑用 (LCT) 来维护原图的一个生成树森林。按顺序加边,当发现两端点已经连通,要形成环时,就删去环上最早加入的一条边。同时用主席树来维护每条边是否在当前的生成树森林中出现。

询问时在 (r) 所对应的主席树上查询区间 ([l,r]) 中有几条边存在,存在的边数即为对应的生成树森林的边数,用 (n) 减去边数即为答案。

因为 (LCT) 维护的是以时间为边权的最大生成树森林,所以每条边都尽可能的选用出现时间晚的,这就使得 (r) 所对应的主席树中区间 ([l,r]) 中边的出现是最多的,所以就保证了正确性。

(code:)

#include<bits/stdc++.h>
#define maxn 400010
#define maxm 12000010
#define inf 1000000000
#define mid ((l+r)>>1)
using namespace std;
template<typename T> inline void read(T &x)
{
    x=0;char c=getchar();bool flag=false;
    while(!isdigit(c)){if(c=='-')flag=true;c=getchar();}
    while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
    if(flag)x=-x;
}
int n,m,q,t,ans,tree_cnt;
int ls[maxm],rs[maxm],cnt[maxm],rt[maxn];
int fa[maxn],ch[maxn][2],rev[maxn],val[maxn],mi[maxn];
struct edge
{
    int x,y;
}e[maxn];
void get(int &l,int &r)
{
    if(t) l=(l+ans)%m+1,r=(r+ans)%m+1;
    if(l>r) swap(l,r);
}
bool check(int x)
{
    return ch[fa[x]][1]==x;
}
bool notroot(int x)
{
    return ch[fa[x]][0]==x||ch[fa[x]][1]==x;
}
void pushup(int x)
{
    mi[x]=val[x];
    mi[x]=min(mi[x],min(mi[ch[x][0]],mi[ch[x][1]]));
}
void pushrev(int x)
{
    rev[x]^=1,swap(ch[x][0],ch[x][1]);
}
void pushdown(int x)
{
    if(!rev[x]) return;
    pushrev(ch[x][0]),pushrev(ch[x][1]),rev[x]=0;
}
void rotate(int x)
{
    int y=fa[x],z=fa[y],k=check(x),w=ch[x][k^1];
    if(notroot(y)) ch[z][check(y)]=x;
    ch[x][k^1]=y,ch[y][k]=w;
    if(w) fa[w]=y;
    fa[x]=z,fa[y]=x;
    pushup(y),pushup(x);
}
void all(int x)
{
    if(notroot(x)) all(fa[x]);
    pushdown(x);
}
void splay(int x)
{
    all(x);
    for(int y;notroot(x);rotate(x))
        if(notroot(y=fa[x]))
            rotate(check(x)^check(y)?x:y);
    pushup(x);
}
void access(int x)
{
    for(int y=0;x;y=x,x=fa[x])
        splay(x),ch[x][1]=y,pushup(x);
}
void makeroot(int x)
{
    access(x),splay(x),pushrev(x);
}
void split(int x,int y)
{
    makeroot(x),access(y),splay(y);
}
int findroot(int x)
{
    access(x),splay(x);
    while(ch[x][0]) x=ch[x][0];
    splay(x);
    return x;
}
void link(int x,int y)
{
	split(x,y),fa[x]=y;
}
void cut(int x,int y)
{
	split(x,y),fa[x]=ch[y][0]=0;
}
int ask(int x,int y)
{
    split(x,y);
    return mi[y];
}
void modify(int l,int r,int pos,int v,int &cur)
{
    int x=++tree_cnt;
    ls[x]=ls[cur],rs[x]=rs[cur],cnt[x]=cnt[cur]+v,cur=x;
    if(l==r) return;
    if(pos<=mid) modify(l,mid,pos,v,ls[cur]);
    else modify(mid+1,r,pos,v,rs[cur]);
}
int query(int L,int R,int l,int r,int cur)
{
    if(L<=l&&R>=r) return cnt[cur];
    int v=0;
    if(L<=mid) v+=query(L,R,l,mid,ls[cur]);
    if(R>mid) v+=query(L,R,mid+1,r,rs[cur]);
    return v;
}
int main()
{
    read(n),read(m),read(q),read(t);
    for(int i=0;i<=n;++i) val[i]=mi[i]=inf;   
    for(int i=1;i<=m;++i) read(e[i].x),read(e[i].y),val[i+n]=mi[i+n]=i;
    for(int i=1;i<=m;++i)
    {
        int x=e[i].x,y=e[i].y;
        rt[i]=rt[i-1];
        if(x==y) continue;
        modify(1,m,i,1,rt[i]);
        if(findroot(x)==findroot(y))
        {
            int p=ask(x,y);
            cut(e[p].x,p+n),cut(e[p].y,p+n);
            modify(1,m,p,-1,rt[i]);
        }
        link(x,i+n),link(y,i+n);
    }
    while(q--)
    {
        int l,r;
        read(l),read(r),get(l,r);
        printf("%d
",ans=n-query(l,r,1,m,rt[r]));
    }
    return 0;
}
原文地址:https://www.cnblogs.com/lhm-/p/13295516.html