星球联盟——题解

题目大意

给出一个N个节点的无向图(不一定联通),Q次加入一条边 X i > Y i ,询问 X i , Y i 是否在一个环中,并求出环中的点数
N , Q <= 200000

无向图的“环联通”?
联通, N 又这么大——自然考虑并查集
关键是又要求要成环——成环=联通+额外的边
可还要统计点数——普通的边合并边累计个数的显然不满足,但可以发现,成环后的点完全可以强制缩成一个大点,不用管他们。换句话说,每个点当且仅当处理一次(大点的老祖宗除外)
那么就可以写个非倍增的LCA来进行暴力缩环,两个点慢慢爬,平摊 O ( N )
过程如下图:
LCA
实现也很简单:

void merge(int x,int y){
    x=getfa(x),y=getfa(y);
    if(x==y) return;
    while(x!=y){
        if(dep[x]<dep[y]) swap(x,y);
        int fx=getfa(x),fy=getfa(pre[fx]);
        //先跳到所在环的老祖宗,再往上跳一步,找到父节点所在环的老祖宗(即目标点)
        if(fx!=fy) cnt[fy]+=cnt[fx],fa[fx]=fy;
        x=fy;
    }
}

所以先将所有边都建好,用并查集进行 O ( 1 ) 检查每条边的“意义”,所谓是“接环边”还是“桥”,然后预处理出点之间的父子关系,供“暴力版LCA”使用
这里还有个问题:原图不连通怎么办?
那就强制找个根连起来建树呗!
复杂度 O ( α ( N + M + Q ) )
PS:无敌的 Z Z K 大佬用 L C T 杀掉了

#include<cstdio>
#include<algorithm>
#include<cassert>
using namespace std;
const int maxn=(2e5)+5,maxE=(6e5)+5;
int n,m,p;
struct Edges{
    int x,y;bool flg;
}E[maxE];

int tot=0,son[maxE],nxt[maxE],lnk[maxn];
void add_e(int x,int y){son[++tot]=y,nxt[tot]=lnk[x],lnk[x]=tot;}

int dep[maxn],pre[maxn];
void DFS(int x,int dad){
    dep[x]=dep[dad]+1,pre[x]=dad;
    for(int j=lnk[x];j;j=nxt[j]) if(!dep[son[j]]) DFS(son[j],x);
}

int fa[maxn],cnt[maxn];
int getfa(int x){return fa[x]==x?x:fa[x]=getfa(fa[x]);}
void merge(int x,int y){
    x=getfa(x),y=getfa(y);
    if(x==y) return;
    while(x!=y){
        if(dep[x]<dep[y]) swap(x,y);
        int fx=getfa(x),fy=getfa(pre[fx]);
        if(fx!=fy) cnt[fy]+=cnt[fx],fa[fx]=fy;
        x=fy;
    }
}

char gt(){
    static char buf[100000],*p1=buf,*p2=buf;
    return p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++;
}
int read(){
    int ret=0;char ch=gt();
    while(ch<'0'||ch>'9') ch=gt();
    while(ch>='0'&&ch<='9') ret=ret*10+ch-'0',ch=gt();
    return ret;
}
void write(int x){if(x<10) putchar(x+'0');else write(x/10),putchar(x%10+'0');}

int main(){
    n=read(),m=read(),p=read();
    for(int i=1;i<=n;i++) fa[i]=i;
    for(int i=1;i<=m+p;i++){
        E[i]=(Edges){read(),read()};
        int x=getfa(E[i].x),y=getfa(E[i].y);
        if(x!=y) fa[x]=y,E[i].flg=1,add_e(E[i].x,E[i].y),add_e(E[i].y,E[i].x);
        //0:接环边  1:桥
    }
    for(int i=1;i<=n;i++) if(getfa(1)!=getfa(i)) add_e(1,getfa(i)),add_e(getfa(i),1),fa[getfa(i)]=getfa(1);
    DFS(1,0);
    for(int i=1;i<=n;i++) fa[i]=i,cnt[i]=1;
    for(int i=1;i<=m;i++) if(!E[i].flg) merge(E[i].x,E[i].y);
    for(int i=m+1;i<=m+p;i++) 
      if(!E[i].flg) merge(E[i].x,E[i].y),write(cnt[getfa(E[i].x)]),putchar('\n');else puts("No");
    return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_42403731/article/details/81037254
今日推荐