训练赛---choice

来源:互联网 发布:js数字转化成中文数字 编辑:程序博客网 时间:2024/06/05 00:10

  • 选择 choice
    • 题目来源
    • 题面
      • 题目描述
      • 输入
      • 数据范围
      • 限制
    • 题解
    • Code

选择 (choice)

题目来源

某集训试题

题面

题目描述

现在我想知道自己是否还有选择。
给定n个点m条边的无向图以及顺序发生的q个事件。
每个事件属于下面两种之一:
1.删除某一条图上仍存在的边;
2.询问是否存在两条边不相交的路径可以从点u出发到点v.

输入

第一行三个整数n,m,q
接下来m行,每行两个整数u,v,表示u和v之间有一条边
接下来q行,每行一个大写字母o和2个整数u,v,依次表示按顺序发生的q个事件:
当o为’Z’时,表示删除一条u和v之间的边
当o为’P’时,表示询问是否存在两条边不相交的路径可以从点u出发到点v
输出:
对于每组询问,如果存在,输出Yes,否则输出No (每组询问的回答用换行符隔开).

数据范围:

20%,max(n,m,p)100  
100%,max(n,m,p)100000  
实测发现,数据保证每个事件中 u!=v.

限制

时间限制: 1s
空间限制: 256M

题解

首先,这个没有强制在线,我们可以把删边转化为倒着加边。
问题在于如何维护.

官方题解给的是用并查集维护。

几个大佬分别采用了:LCA+并查集,并查集+乱搞,并查集+tarjan(边双联通分量)的做法,都有一定的道理,而且效率很高。

不过作为一个蒟蒻,我想到的做法是LCT. 虽然可以A,但是常数巨大,极限数据用时是标解的5倍左右,是时限的0.6倍左右.

怎么做呢?
原图上的点在LCT上的初始权值均设为1.
每当加入u->v边的时候,分两类情况讨论:

  • u,v尚未联通.此时利用并查集维护联通性(LCT亦可,常数较大)。将u,v在并查集中连通。设立新节点node. 之后在LCT中,link(u,node),link(node,v). 新节点的初始权值为0.这实际上相当于加入一条权值为0的边.

  • u,v已经联通.此时将LCT中u,v链上的所有点权值改为1.

每当询问u,v的时候,我们判断一下:

  • 如果u,v不联通,输出0.

  • 如果u,v联通,在LCT中查询u,v链上所有点的最小权值.若最小值为0,则输出no;若最小值为1,则输出yes.

这么做的正确性基于几个基本事实:

  • 如果u,v之间有两条边不相交的路径,那么u,v路径上任意两点之间都有两条边不相交的路径.
  • 如果 i 到 j 满足题意, j 到 k 满足题意,那么i到k也是满足题意的.
  • LCT中权值为0的边 (其实是权值为0的虚拟节点) 即是求双联通分量的时候遇到的桥边.
  • 连在已联通的树链上的边实际上就是tarjan中的返祖边.

于是就相当于是用LCT来干了一件本来应该用并查集和tarjan完成的事。

这个题读数有一些诡异,读入的是删除的边的两个端点。我开了两个堆来干这件事.先将所有边压入堆1中,将要删的边压入堆2中,然后就像在堆中删除元素那样搞就行了。

Code

#include<cstdio> #include<cstring> #include<iostream> #include<algorithm> #include<queue> using namespace std; #define MAXN 255000 void _r(int& x) {     char c=getchar();     while(c<'0'||c>'9')     {         c=getchar();     }     for(x=0;c>='0'&&c<='9';c=getchar())     {         x=(x<<1)+(x<<3)+c-'0';     }     return ; } struct node {     int x,y,c;     node(int a=0,int b=0,int cc=0)     {         x=a;         y=b;         c=cc;     }     bool operator < (const node& p) const     {         return (x==p.x)?(y<p.y):(x<p.x);     }     bool operator == (const node& p) const     {         return x==p.x&&y==p.y;     } }E[MAXN]; int Ans[MAXN],tot; priority_queue<node>P1; priority_queue<node>P2; int n,m,Q; char op[10]; int ch[MAXN][2],tag[MAXN],rev[MAXN],val[MAXN],mn[MAXN],fa[MAXN]; int st[MAXN],top; bool notroot(int p) {     return ch[fa[p]][0]==p||ch[fa[p]][1]==p; } void cal(int p) {     int ls=ch[p][0],rs=ch[p][1];     int mm=val[p];     if(ls) //注意判一下,不然mm可能会意外地变成0;    {         mm=min(mm,mn[ls]);     }     if(rs)     {         mm=min(mm,mn[rs]);     }     mn[p]=mm;     return ; } inline void zg(int x,int d) {     int y=fa[x],z=fa[y];     if(notroot(y))     {         if(ch[z][0]==y)         {             ch[z][0]=x;         }         else         {             ch[z][1]=x;         }     }     fa[x]=z;     ch[y][d]=ch[x][!d];     fa[ch[y][d]]=y;     ch[x][!d]=y;     fa[y]=x;     cal(y);     cal(x);     return ; } inline void push(int p) {     int ls=ch[p][0],rs=ch[p][1];     if(tag[p])     {         tag[p]=0;         tag[ls]=tag[rs]=1;         val[ls]=val[rs]=mn[ls]=mn[rs]=1;     } //标记下放顺序无所谓;    if(rev[p])     {         rev[p]=0;         rev[ls]^=1;         rev[rs]^=1;         swap(ch[p][0],ch[p][1]);     }     return ; } inline void splay(int x) {     int y,z;     st[++top]=x;     for(int i=x;notroot(i);i=fa[i])     {         st[++top]=fa[i]; //之前把fa[i]敲成了fa[x],调了很久才发现是这个问题    }     for(;top;--top)     {         push(st[top]);     }     while(notroot(x))     {         y=fa[x];         z=fa[y];         if(notroot(y))         {             if(ch[z][0]==y^ch[y][0]==x)             {                 zg(x,ch[fa[x]][1]==x);             }             else             {                 zg(y,ch[fa[y]][1]==y);             }         }         zg(x,ch[fa[x]][1]==x);     }     return ; } inline void access(int x) {     for(int t=0;x;t=x,x=fa[x])     {         splay(x);         ch[x][1]=t;         cal(x);     }     return ; } inline void makeroot(int x) {     access(x);     splay(x);     rev[x]^=1;     return ; } void link(int x,int y) {     makeroot(x);     fa[x]=y;     return ; } void change(int x,int y) {     makeroot(x);     access(y);     splay(y);     tag[y]=1;     val[y]=mn[y]=1; //这里可以不动mn[y];    return ; } int F[MAXN],N; int getfa(int x) {     return F[x]==x?x:F[x]=getfa(F[x]); } inline int query(int x,int y) {     if(getfa(x)!=getfa(y))    {        return 0;    }    makeroot(x);     access(y);     splay(y);     return mn[y]; } inline void Link(int x,int y) {     int fx,fy;     fx=getfa(x);     fy=getfa(y);     if(fx!=fy)     {         ++N;//新建节点,初值默认为0;        link(x,N);        link(N,y);         F[fx]=fy;     }     else     {         change(x,y);     }     return ; } int main() {     //freopen("choice.in","r",stdin);    //freopen("choice.out","w",stdout);    _r(n);     _r(m);     _r(Q);     for(int i=1,x,y;i<=m;i++)     {         _r(x);         _r(y);         if(x>y)         {             swap(x,y);         }         P1.push(node(x,y));     }     for(int i=1,x,y;i<=Q;i++)     {         scanf("%s",op);         _r(x);         _r(y);         if(x>y)         {             swap(x,y);         }         if(op[0]=='Z')         {             P2.push(node(x,y));             E[i]=node(x,y,0);         }         else         {             E[i]=node(x,y,1);         }     }     node tmp;     for(int i=1;i<=n;i++)     {         F[i]=i;        val[i]=1;    }     N=n;    while(P1.size())     {         while(P2.size()&&(P2.top()==P1.top()))         {             P2.pop();             P1.pop();         }        if(P1.size())//这里一定要判!        {             tmp=P1.top();             P1.pop(); //先前忘写这句,直接跑死            Link(tmp.x,tmp.y);         }    }     for(int i=Q;i>=1;i--)     {         tmp=E[i];         if(tmp.c==1)         {             Ans[i]=query(tmp.x,tmp.y);         } //询问        else         {             Link(tmp.x,tmp.y);             Ans[i]=-1;         } //加边    }     for(int i=1;i<=Q;i++)     {         if(Ans[i]>=0)         {             if(Ans[i]==0)             {                 printf("No\n");             }             else             {                 printf("Yes\n");             }         }     }     return 0; }