LCT例题·BZOJ2049洞穴勘测、BZOJ3669魔法森林
来源:互联网 发布:淘宝网的域名怎么设置 编辑:程序博客网 时间:2024/05/21 11:33
LCT例题·BZOJ2049洞穴勘测、BZOJ3669魔法森林
LCT是个好东西。
BZOJ2049洞穴勘测
原题地址:http://www.lydsy.com/JudgeOnline/problem.php?id=2049
题意:
n个点m个操作,操作有三种
1.Connect 连接两个点;
2.Destroy 删除两个点之间的连接;
3.Query 查询两个点是否连通;
题解:
LCT入门,只有几个基础操作
%黄学长
代码:
#include<cstdio>#include<iostream>#include<algorithm>#include<cstring>using namespace std;const int N=10005;struct node{ int fa,ch[2]; bool rez; };node tr[N];int n,m;struct Link_Cut_Tree{ void init(int x) { tr[x].fa=tr[x].ch[0]=tr[x].ch[0]=0; tr[x].rez=0; } bool isroot(int x) //是否是该条链的splay的root { return (tr[tr[x].fa].ch[0]!=x)&&(tr[tr[x].fa].ch[01]!=x); } void pushdown(int x) { if(tr[x].rez) { swap(tr[x].ch[0],tr[x].ch[1]); tr[x].rez^=1; tr[tr[x].ch[0]].rez^=1; tr[tr[x].ch[1]].rez^=1; return; } } void push(int x) { if(!isroot(x)) push(tr[x].fa); pushdown(x); } void rotate(int x) { int y=tr[x].fa; int z=tr[y].fa; int l,r; if(tr[y].ch[0]==x) l=0; else l=1; r=l^1; if(!isroot(y)) { if(tr[z].ch[0]==y) tr[z].ch[0]=x; else tr[z].ch[1]=x; } tr[y].ch[l]=tr[x].ch[r]; tr[x].ch[r]=y; tr[x].fa=z; tr[y].fa=x; tr[tr[y].ch[l]].fa=y; // update(y); update(x); } void splay(int x) { push(x); while(!isroot(x)) { int y=tr[x].fa; int z=tr[y].fa; if(!isroot(y)) { if((tr[z].ch[0]==y)^(tr[y].ch[1]==x)) rotate(y);////////zag else rotate(x);/////////////zig } rotate(x); } } void access(int x) { int y=0; for(;x;y=x,x=tr[x].fa) { splay(x); tr[x].ch[1]=y;//y的深度>x,y成为x的右儿子 //update(x); } } int findroot(int x) { access(x); splay(x); while(tr[x].ch[0]) x=tr[x].ch[0]; return x; } void rever(int x) { access(x); splay(x); tr[x].rez^=1; } void link(int x,int y) { if(findroot(x)==findroot(y)) {puts("-1");return;} rever(x); tr[x].fa=y; } void cut(int x,int y) { if(findroot(x)!=findroot(y)) {puts("-1");return;} rever(x); access(y); splay(y); //先把X搞成根了 所以深度较小 tr[y].ch[0]=tr[x].fa=0; //update(y);//////////// } void query(int x,int y) { if (findroot(x)==findroot(y)) printf("Yes\n"); else printf("No\n"); }}LCT;int main(){ scanf("%d%d",&n,&m); for(int i=0;i<=n;i++) { LCT.init(i); } while(m--) { char opt[10]; int u,v; scanf("%s",opt); scanf("%d%d",&u,&v); if(opt[0]=='C') { LCT.link(u,v); } else if(opt[0]=='D') { LCT.cut(u,v); } else LCT.query(u,v); } return 0;}
BZOJ3669魔法森林
原题地址:http://www.lydsy.com/JudgeOnline/problem.php?id=2049
题意:给定一个图,每条边有ai、bi两种路径,选择一条1~n的路径,使得这条路径上max(ai)+max(bi)最小。
题解:这道题非常妙的一点在于把边看做一个点,连接着这条边的左右端点。
其次,先把ai排序,按照类似与做最小生成树的方法依次添加,若左右端点未联通,则直接link(端点u,代表边的节点)、link(端点v,代表边的节点)。若联通,则判断当前u-v路径上的最大bi值是否大于当前bi,若是,断原边,连现边。当达到1~n连通后,每次比较当前ans与记录的最小ans即可。
之前为什么更新时是直接比较b值有些不理解,其实是这样:
考虑直接用比较b值的正确性:
1、 如果加入这条边前1,N不连通,则后面一定会加入边,由于a值递增,后面的a值比现在两条边的a 值都要大,因此当前边的a值大小不会影响。
2、如果加入这条边前1,N已联通,则之前的最小答案已经记录,不会对最终答案进行影响。
ps.这里学长对ai<=30,bi<=50000的子问题给了个思路,固定一个量,枚举ai,对满足条件的边的bi构造最小生成树。
代码:
#include<cstdio>#include<iostream>#include<algorithm>#include<cstring>using namespace std;const int N=50005;const int M=100005;int val[M+N];const int inf=0x7fffffff;int ans;struct eadge{ int u,v,a,b;}e[M];bool cmp(const eadge &A,const eadge &B){ return A.a<B.a;}struct node{ int ch[2],fa; int pos,maxn; bool rev;};node tr[M+N];struct Link_Cut_Tree{ void init(int x) { tr[x].fa=tr[x].ch[0]=tr[x].ch[1]=tr[x].pos=tr[x].maxn=tr[x].rev=0; } void pushdown(int x) { if(!x) return; if(tr[x].rev) { swap(tr[x].ch[0],tr[x].ch[1]); tr[tr[x].ch[0]].rev^=1; tr[tr[x].ch[1]].rev^=1; tr[x].rev^=1; } } void push(int x) { if(!isroot(x)) push(tr[x].fa); pushdown(x); } void update(int x) { if(!x) return; tr[x].pos=x; tr[x].maxn=val[x]; //因为其左右子树都有可能更改,因此不能根据ch[0/1]更改,而是从自己val开始 if(tr[x].ch[0]) if(val[tr[x].pos]<val[tr[tr[x].ch[0]].pos]) {tr[x].maxn=tr[tr[x].ch[0]].maxn; tr[x].pos=tr[tr[x].ch[0]].pos;} if(tr[x].ch[1]) if(val[tr[x].pos]<val[tr[tr[x].ch[1]].pos]) {tr[x].maxn=tr[tr[x].ch[1]].maxn; tr[x].pos=tr[tr[x].ch[1]].pos;} } bool isroot(int x) { return (tr[tr[x].fa].ch[0]!=x)&&(tr[tr[x].fa].ch[1]!=x); } void rotate(int x) { int y=tr[x].fa; int z=tr[y].fa; int l,r; if(tr[y].ch[0]==x) l=0; else l=1; r=l^1; if(!isroot(y)) { if(tr[z].ch[0]==y) tr[z].ch[0]=x; else tr[z].ch[1]=x; } tr[y].ch[l]=tr[x].ch[r]; tr[tr[x].ch[r]].fa=y; tr[x].ch[r]=y; tr[y].fa=x; tr[x].fa=z; update(y); update(x); } void splay(int x) { push(x); while(!isroot(x)) { int y=tr[x].fa; int z=tr[y].fa; if(!isroot(y)) { if((tr[z].ch[0]==y)^(tr[y].ch[0]==x)) rotate(y); else rotate(x); } rotate(x); } } void access(int x) { int y=0; for(;x;y=x,x=tr[x].fa) { splay(x); tr[x].ch[1]=y; update(x); } } int findroot(int x) { access(x); splay(x); while(tr[x].ch[0]!=0) x=tr[x].ch[0]; return x; } void rever(int x) { access(x);splay(x); tr[x].rev^=1; } void link(int x,int y) { rever(x); tr[x].fa=y; } void cut(int x,int y) { if(findroot(x)!=findroot(y)) return; rever(x); access(y); splay(y); tr[x].fa=tr[y].ch[0]=0; update(y); } int query(int x,int y) { rever(x); access(y); splay(y); return tr[y].pos; }}LCT;int main(){ int n,m; scanf("%d%d",&n,&m); memset(val,0,sizeof(val)); for(int i=1;i<=m;i++) { scanf("%d%d%d%d",&e[i].u,&e[i].v,&e[i].a,&e[i].b); } sort(e+1,e+m+1,cmp); for(int i=1;i<=m;i++) { val[n+i]=e[i].b; } for(int i=0;i<=n+m;i++) { LCT.init(i); } ans=inf; for(int i=1;i<=m;i++) { int fx=LCT.findroot(e[i].u); int fy=LCT.findroot(e[i].v); if(fx!=fy) { LCT.link(e[i].u,i+n); LCT.link(i+n,e[i].v); } else { int k=LCT.query(e[i].u,e[i].v); if(val[k]>e[i].b) { LCT.cut(e[i].u,k); LCT.cut(e[i].v,k); LCT.link(e[i].u,i+n); LCT.link(e[i].v,i+n); } } if(LCT.findroot(1)==LCT.findroot(n)) ans=min(ans,val[LCT.query(1,n)]+e[i].a); } if(ans!=inf) printf("%d",ans); else printf("-1"); return 0;}
最后安利dalao的浅谈LCT:
http://blog.csdn.net/NOIAu/article/details/75451616
写得真心好。
- LCT例题·BZOJ2049洞穴勘测、BZOJ3669魔法森林
- bzoj2049 洞穴勘测 lct
- BZOJ2049 洞穴勘测 LCT
- 【bzoj2049】【LCT】洞穴勘测
- 【bzoj2049】【SDOI2008】【洞穴勘测】【lct】
- 【bzoj2049】Cave洞穴勘测 LCT
- 【BZOJ2049】洞穴勘测(SDOI2008)-LCT真·模板题
- bzoj2049: [Sdoi2008]Cave 洞穴勘测 LCT
- 【LCT】BZOJ2049[Sdoi2008]Cave 洞穴勘测
- bzoj2049[洞穴勘测]纯粹的LCT
- [BZOJ2049][SDOI2008]Cave 洞穴勘测(LCT)
- [BZOJ2049][[Sdoi2008]Cave 洞穴勘测][LCT]
- LCT裸题-[BZOJ2049][Sdoi2008]Cave 洞穴勘测
- bzoj2049: [Sdoi2008]Cave 洞穴勘测(lct)
- bzoj2049 [Sdoi2008]Cave 洞穴勘测 [LCT]
- bzoj3669: [Noi2014]魔法森林 LCT
- [BZOJ3669][Noi2014]魔法森林 && LCT
- 【bzoj3669】[Noi2014]魔法森林 LCT
- UVALive 3713 浅谈2-SAT问题图论求解法
- BZOJ 4241 历史研究 (回滚莫队)
- Android ViewPager用法解析
- JDBC高级编程和DAO
- spring学习之---深入理解容器中的Bean
- LCT例题·BZOJ2049洞穴勘测、BZOJ3669魔法森林
- Lexicographically Maximum Subsequence CodeForces
- 1055. The World's Richest (25)
- 将需要人为触发的事件放在定时器或者循环中,程序有可能产生Bug
- POI操作Excel
- 【玩转Eclipse】——eclipse实现代码块自定义折叠---[类似于VS中的#region……#endregion]
- 《剑指offer》笔记-第4章(2)
- 多维动态规划 (共六题)
- BZOJ4216【Pig】