[bzoj4998][LCT][并查集]星球联盟

18 篇文章 0 订阅
10 篇文章 0 订阅

Description

在遥远的S星系中一共有N个星球,编号为1…N。其中的一些星球决定组成联盟,以方便相互间的交流。但是,组成
联盟的首要条件就是交通条件。初始时,在这N个星球间有M条太空隧道。每条太空隧道连接两个星球,使得它们能
够相互到达。若两个星球属于同一个联盟,则必须存在一条环形线路经过这两个星球,即两个星球间存在两条没有
公共隧道的路径。为了壮大联盟的队伍,这些星球将建设P条新的太空隧道。这P条新隧道将按顺序依次建成。一条
新轨道建成后,可能会使一些星球属于同一个联盟。你的任务是计算出,在一条新隧道建设完毕后,判断这条新轨
道连接的两个星球是否属于同一个联盟,如果属于同一个联盟就计算出这个联盟中有多少个星球。

Input

第1行三个整数N,M和P,分别表示总星球数,初始时太空隧道的数目和即将建设的轨道数目。
第2至第M+1行,每行两个整数,表示初始时的每条太空隧道连接的两个星球编号。
第M+2行至第M+P+1行,每行两个整数,表示新建的太空隧道连接的两个星球编号。 这些太空隧道按照输入的顺序依次建成。
1≤N,M,P≤200000

Output

输出共P行。 如果这条新的太空隧道连接的两个星球属于同一个联盟,就输出一个整数,表示这两个星球所在联盟的星球数。
如果这条新的太空隧道连接的两个星球不属于同一个联盟,就输出”No”(不含引号)。

Sample Input

5 3 4

1 2

4 3

4 5

2 3

1 3

4 5

2 4

Sample Output

No

3

2

5

HINT

这里写图片描述

题解

感觉还是很好想的啊..
LCT维护森林
每次加进来的边要不就是树边要不就是非树边
树边直接加上,并查集维护连通性直接LCT太慢啦啦啦
非树边就会成环,设这条边是(x,y)
把x~y的路径提取出来,就是这个环
再用一个并查集维护点属于的边双编号
暴力dfs维护并查集
别人的LCT模板怎么这么好看啊

不管了以后我的模板就是这个

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<cmath>
using namespace std;
int fa1[210000],fa2[210000],siz[210000];
int findfa1(int x){return fa1[x]==x?fa1[x]:fa1[x]=findfa1(fa1[x]);}
int findfa2(int x){return fa2[x]==x?fa2[x]:fa2[x]=findfa2(fa2[x]);}
struct lct
{
    int son[2],f;
    int fz;
}tr[210000];
bool isroot(int x){return tr[findfa1(tr[x].f)].son[0]!=x&&tr[findfa1(tr[x].f)].son[1]!=x;}
void pushdown(int x)
{
    if(tr[x].fz)
    {
        swap(tr[x].son[0],tr[x].son[1]);
        if(tr[x].son[0])tr[tr[x].son[0]].fz^=1;
        if(tr[x].son[1])tr[tr[x].son[1]].fz^=1;
        tr[x].fz=0;
    }
}
void update(int x)
{
    if(!isroot(x))update(findfa1(tr[x].f));
    pushdown(x);
}
void rotate(int x)
{
    int y=findfa1(tr[x].f),z=findfa1(tr[y].f),d=(x==tr[y].son[1]);
    if(!isroot(y))tr[z].son[y==tr[z].son[1]]=x;
    tr[y].f=x;tr[x].f=z;tr[y].son[d]=tr[x].son[d^1];
    if(tr[x].son[d^1])tr[tr[x].son[d^1]].f=y;
    tr[x].son[d^1]=y;
}
void splay(int x)
{
    update(x);
    while(!isroot(x))
    {
        int y=findfa1(tr[x].f),z=findfa1(tr[y].f);
        if(!isroot(y))
        {
            if((x==tr[y].son[0])^(y==tr[z].son[0]))rotate(x);
            else rotate(y);
        }
        rotate(x);
    }
}
void access(int x)
{
    int y=0;
    while(x!=0)
    {
        splay(x);
        tr[x].son[1]=y;
        if(y)tr[y].f=x;
        y=x;x=findfa1(tr[x].f);
    }
}
void markroot(int x){access(x);splay(x);tr[x].fz^=1;}
void link(int x,int y){markroot(x);tr[x].f=y;access(x);}
int sum;
void play(int x,int y)
{
    if(!x)return ;
    sum+=siz[x];
    if(x!=y)siz[y]+=siz[x],fa1[x]=y;
    play(tr[x].son[0],y);play(tr[x].son[1],y);
}
void lk(int x,int y)
{
    sum=0;
    if(findfa2(x)!=findfa2(y))
    {
        fa2[fa2[x]]=fa2[y];
        link(x,y);
    }
    else
    {
        markroot(x);access(y);splay(y);
        play(y,y);tr[y].son[0]=tr[y].son[1]=0;
    }
}
int n,m,P;
int main()
{
//  freopen("a.in","r",stdin);
//  freopen("a.out","w",stdout);
    scanf("%d%d%d",&n,&m,&P);
    for(int i=1;i<=n;i++)fa1[i]=i,fa2[i]=i,siz[i]=1;
    for(int i=1;i<=m;i++)
    {
        int x,y;scanf("%d%d",&x,&y);
        x=findfa1(x);y=findfa1(y);
        lk(x,y);
    }
    while(P--)
    {
        int x,y;scanf("%d%d",&x,&y);
        x=findfa1(x);y=findfa1(y);
    /*  int uu=findfa(x),vv=findfa(y);
        printf("%d     ",getroot(uu));
        printf("%d\n",getroot(vv));*/
        lk(x,y);
        if(sum==0)puts("No");
        else printf("%d\n",sum);
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值