训练赛---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 (每组询问的回答用换行符隔开).
数据范围:
实测发现,数据保证每个事件中 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; }
- 训练赛---choice
- CHOICE
- Choice
- choice
- list-choice
- choice 详解
- choice 命令
- my choice
- random.choice
- 训练赛
- 训练集--训练赛8
- 训练集---训练赛9
- 训练集---训练赛10
- 训练集---训练赛11
- 训练集---训练赛12
- 训练集---训练赛13
- 训练集---训练赛14
- Dropout is our choice...
- 五月英语学习总结
- Wireshark抓包详细图文教程
- maven打包不用eclipse插件
- lankuohsing的CSDN博客开张大吉
- Sublime常用快捷键
- 训练赛---choice
- 机器学习知识总结:代价函数与经验风险、结构风险最小化
- android gradle配置
- c++的最基础的类和对象
- hibernate-性能优化(3种缓存)-19
- P1605 迷宫
- ZigZag Conversion
- PHP中常用的数组函数
- 搜集关于TCP/IP 一些链接