luogu 2542 [AHOI2005]航线规划

题目链接:luogu2542

先考虑没有修改操作应该如何完成询问:使用\(Tarjan\)对点双进行缩点,答案就是缩完点后两点之间的距离

再考虑有修改的情况,注意到修改只有删除一种操作,于是可以考虑倒序加边

使用\(LCT\)维护这个加边的过程,考虑每一次加边对图中点双的影响:如果连成了一个环,那么就将这个环缩成一个点

采用线段树打标记的思想,我们再开一个并查集,来维护缩点之后每个点的编号。具体的,对于每次加边,我们先找到连边的两点在缩完点之后的编号,若不在一个连通块的话则直接连边,否则就要考虑在并查集上打上缩点的tag。注意到我们\(makeroot(x),access(y)\)之后这棵splay维护的就是\(x->y\)的链了,于是直接暴力的将\(x\)的右儿子清空,并在并查集上将它们的父亲置为\(x\)

但这只是一个打标记的过程,我们并没有对子树信息做实质性的修改

由于查询子树信息时都需要\(access\),于是可以在这个上面动手,将原来的\(x=fa[x]\)变成\(fa[x]\)缩成的那个点即可

具体的更多细节看代码

#include<iostream>
#include<string.h>
#include<string>
#include<stdio.h>
#include<algorithm>
#include<vector>
#include<math.h>
#include<queue>
#include<set>
#include<map>
using namespace std;
typedef long long ll;
typedef long double db;
const int N=10000;
const db pi=acos(-1.0);
#define lowbit(x) (x)&(-x)
#define sqr(x) (x)*(x)
#define rep(i,a,b) for (register int i=a;i<=b;i++)
#define per(i,a,b) for (register int i=a;i>=b;i--)
#define fir first
#define sec second
#define mp(a,b) make_pair(a,b)
#define pb(a) push_back(a)
#define maxd 998244353
#define eps 1e-8
struct node{
    int u,v;
}edge[200200];
struct qnode{
    int op,x,y;
}q[200200];
int n,m,siz[30050],fa[30030],ch[30030][2],pa[30030],tag[30030],sta[30030],tot=0,tot1=0,ans[40030];
map<pair<int,int>,int> used;
int read()
{
    int x=0,f=1;char ch=getchar();
    while ((ch<'0') || (ch>'9')) {if (ch=='-') f=-1;ch=getchar();}
    while ((ch>='0') && (ch<='9')) {x=x*10+(ch-'0');ch=getchar();}
    return x*f;
}

bool isroot(int x) {return ((ch[fa[x]][0]!=x) && (ch[fa[x]][1]!=x));}
void pushup(int x) {siz[x]=siz[ch[x][0]]+siz[ch[x][1]]+1;}
void puttag(int x) {swap(ch[x][0],ch[x][1]);tag[x]^=1;}
int find(int x) {if (pa[x]==x) return x;pa[x]=find(pa[x]);return pa[x];}
void pushdown(int x)
{
    if (tag[x])
    {
        if (ch[x][0]) puttag(ch[x][0]);
        if (ch[x][1]) puttag(ch[x][1]);
        tag[x]=0;
    }
}

void rotate(int x)
{
    int y=fa[x],z=fa[y],k=(ch[y][1]==x),w=ch[x][k^1];
    if (!isroot(y)) ch[z][ch[z][1]==y]=x;
    ch[x][k^1]=y;ch[y][k]=w;
    if (w) fa[w]=y;fa[y]=x;fa[x]=z;
    pushup(y);pushup(x);
}

void splay(int x)
{
    int now=x,tp=0;sta[++tp]=now;
    while (!isroot(now)) {now=fa[now];sta[++tp]=now;}
    while (tp) {pushdown(sta[tp]);tp--;}
    while (!isroot(x))
    {
        int y=fa[x],z=fa[y];
        if (!isroot(y))
        {
            if ((ch[y][0]==x)^(ch[z][0]==y)) rotate(x);else rotate(y);
        }
        rotate(x);
    }
    pushup(x);
}

void access(int x)
{
    int y=0;
    while (x)
    {
        splay(x);ch[x][1]=y;pushup(x);
        y=x;fa[y]=find(fa[y]);x=find(fa[x]);
    }
}

int findroot(int x)
{
    access(x);splay(x);
    while (ch[x][0])
    {
        pushdown(x);x=ch[x][0];
    }
    splay(x);return x;
}

void makeroot(int x) {access(x);splay(x);puttag(x);}
void split(int x,int y) {makeroot(x);access(y);splay(y);}
void del(int x,int fx) {if (x) {pa[x]=fx;del(ch[x][0],fx);del(ch[x][1],fx);}}
void link(int x,int y)
{
    if (x==y) return;
    makeroot(x);
    if (findroot(y)!=x)
    {
        fa[x]=y;pushup(y);
    }
    else
    {   
        del(ch[x][1],x);
        ch[x][1]=0;pushup(x);
    }
}

int main()
{
    n=read();m=read();
    rep(i,1,m)
    {
        edge[i].u=read();edge[i].v=read();
    }
    while (1)
    {
        int op=read();if (op==-1) break;
        q[++tot].op=op;
        q[tot].x=read();q[tot].y=read();
        if (!op) used[mp(q[tot].x,q[tot].y)]=used[mp(q[tot].x,q[tot].y)]=1;
    }
    rep(i,1,n) {siz[i]=1;pa[i]=i;}
    rep(i,1,m)
    {
        if (used.count(mp(edge[i].u,edge[i].v))==0) 
        {
            int fx=find(edge[i].u),fy=find(edge[i].v);
            link(fx,fy);
        }
    }
    per(i,tot,1)
    {
        int fx=find(q[i].x),fy=find(q[i].y);
        if (q[i].op)
        {
            split(fx,fy);ans[++tot1]=siz[fy]-1;
        }
        else link(fx,fy);
        //rep(j,1,n) cout << find(j) << " ";cout << endl;
    }
    per(i,tot1,1) printf("%d\n",ans[i]);
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/encodetalker/p/11071755.html
今日推荐