codeforces 603E(LCT+优先队列)
来源:互联网 发布:甬航数据交互平台 编辑:程序博客网 时间:2024/06/04 20:01
我在比赛(noip模拟)时看到这题,一脸懵逼,上数学课想了很久也想不完整,总有地方实现不了。后来知道了某个dalao用LCT过的,然后和kscla吹B了半个晚上,就把这题吹出来了。
先讲LCT部分,朴素的LCT可以支持在有Link和Cut操作的森林上修改路径和维护路径信息,比如最大值。
那么就可以维护只有加边情况下的动态最小生成树。
(就是假若出现环,就把环上最大的一条边Cut掉)
Lct还可以维护连通块大小。由于LCT是维护出来的森林是有序的,即一条树链上的结点在Splay上的顺序就是深度由小到大的顺序。而path-parent也是parent,对于一条没有path-parent的路径,它的第一个结点就是这个连通块的根。
既然树是有序的,也就有了相对的子树,我们就可以像静态树一样由下往上维护子树大小。
所以朴素的想法就是按顺序,每条路径将自身子树大小传给path-parent。
可以每个结点开一个other值,表示所有path-parent指向这个点的路径所在子树大小的和。
splay可以维护other的和,记为sum,splay根上的sum+(splay大小)即为这个连通块的大小。
由此也可知,对于只在splay上的操作(不涉及path-parent的更改,如,splay,Cut中的删儿子,翻转),是不会影响other的sum的正确信息的。
现在考虑更改path-parent的操作
Access:Access就是按由下到上顺序把一个点到根的路径扔进splay里,所以就按顺序统计把当前sum算进other里。
Link(x,y):先Evert(x),再splay(x),那么x就能得到x所在连通块正确的信息。
x->path-parent=y。大概想法就是用x所在连通块的信息更新y,y所在splay的path-parent,…,一路更新到根。这样其实不影响复杂度,因为相当于Access(y)。(当然也可以Access(y)后更新一次)
如果学过LCT的套路,以上就是废话了。
对于这题,sunny图的充要条件是每个连通块都有偶数个点。(每个连通块保留一棵生成树,对于每条树边,若该边相连的两个连通块大小都为偶数,则不选该边,否则就选)。
本蒟蒻考试时这点都没看出来……
LCT题的套路:答案在最小生成树上
对于边(x,y),若存在路径(x,y),无论Cut掉路径上哪一条边,边(x,y)与被Cut掉的边都是等价的,同时选或不选。即若不Cut掉的话,当某条边作为答案时,答案需与另一条边取max,故Cut掉长度最大的边最优 。
统计答案用一个维护最大值的优先队列,存树上的边。每次操作后
不断重复:若队头不在树上,或者相连的两个连通块大小都为偶数,则把队头弹出。
这样后,队头就是答案。
我LCT,洋洋洒洒300行,还请教练把时限开大了才能A。
#include <iostream>#include <fstream>#include <algorithm>#include <cmath>#include <ctime>#include <cstdio>#include <cstdlib>#include <cstring>#include <queue>using namespace std;#define mmst(a, b) memset(a, b, sizeof(a))#define mmcp(a, b) memcpy(a, b, sizeof(b))#define imax(a, b) (((a)>(b)) ? (a) : (b))typedef long long LL;const int N=800800;void read(int &hy){ hy=0; char cc=getchar(); while(cc>'9'||cc<'0') cc=getchar(); while(cc>='0'&&cc<='9') { hy=(hy<<3)+(hy<<1)+cc-'0'; cc=getchar(); }}int js,cnt,n,m,u[N],v[N],len[N],fa[N];int find(int x){ if(fa[x]!=x) fa[x]=find(fa[x]); return fa[x];}struct yy{ int num,len; bool operator < (const yy &a) const{return len<a.len;}}tu;priority_queue<yy>q;struct tree{ tree *c[2],*f,*pp; int e,me; bool flip,other,sum,siz,a; int d(){return f->c[1]==this;} void sc(tree *x,int d){(c[d]=x)->f=this;}}nil[N],*ro[N],*edge[N];inline tree *newtree(int e,bool a){ nil[++cnt]=nil[0]; nil[cnt].sum=nil[cnt].siz=nil[cnt].a=a; nil[cnt].me=nil[cnt].e=e; return nil+cnt;}inline void up(tree *x){ x->siz=x->c[0]->siz^x->c[1]->siz^x->a; x->me=imax(x->e,imax(x->c[0]->me,x->c[1]->me)); x->sum=x->a^x->other^x->c[0]->sum^x->c[1]->sum;}inline void down(tree *x){ if(!x->flip) return; x->flip=0; swap(x->c[0],x->c[1]); x->c[0]->flip^=1; x->c[1]->flip^=1;}void work(tree *x){ if(x->f!=nil) work(x->f); down(x);}inline void zig(tree *x){ tree *y=x->f; int d=x->d(); y->sc(x->c[!d],d); if(y->f==nil) x->f=nil; else y->f->sc(x,y->d()); x->sc(y,!d); x->pp=y->pp; y->pp=nil; up(y);}inline void splay(tree *x){ work(x); for(tree *y;x->f!=nil;) { y=x->f; if(y->f!=nil) (x->d() ^ y->d()) ? zig(x) : zig(y); zig(x); } up(x);}inline void Access(tree *x){ tree *y=nil; while(x!=nil) { splay(x); if(x->c[1]!=nil) { x->c[1]->f=nil; x->c[1]->pp=x; x->other^=x->c[1]->sum; } x->c[1]=y; if(y!=nil) y->f=x; x->other^=y->sum; y->pp=nil; up(x); y=x; x=x->pp; }}inline void Evert(tree *x){ Access(x); splay(x); x->flip^=1;}inline void Link(tree *x,tree *y){ Evert(x); splay(x); Access(y); splay(y); x->pp=y; y->other^=x->sum; up(y);}inline void Cut(tree *x,tree *y){ Evert(x); Access(y); splay(x); x->c[1]->f=nil; x->c[1]=nil; up(x);}void unite(int num,int X,int Y){ tree *x=ro[X]; tree *y=ro[Y]; X=find(X); Y=find(Y); tu.num=num; tu.len=len[num]; if(X^Y) { if(js!=0) { Access(x); splay(x); Access(y); splay(y); if(x->sum&&y->sum) js-=2; } Link(edge[num],x); Link(edge[num],y); fa[X]=Y; q.push(tu); return; } Evert(x); Access(y); splay(x); int le=x->me; if(le<=len[num]) return; tree *xx=x; while(1) { if(xx->e==le) break; if(xx->c[0]->me==le) xx=xx->c[0]; else xx=xx->c[1]; } Cut(ro[u[xx-nil]],xx); Cut(ro[v[xx-nil]],xx); len[xx-nil]=0; Link(x,edge[num]); Link(y,edge[num]); fa[X]=Y; q.push(tu);}void sora(){ bool hy=true; while(hy&&!q.empty()) { hy=false; tu=q.top(); if(!len[tu.num]) { q.pop(); hy=true; } else { tree *x=ro[u[tu.num]]; Cut(x,edge[tu.num]); Evert(x); if(!x->sum) { q.pop(); hy=true; } Link(x,edge[tu.num]); } }}int main(){ nil->c[0]=nil->c[1]=nil->f=nil->pp=nil; cin>>n>>m; for(int i=1;i<=n;i++) fa[i]=i; if(n&1) { for(int i=1;i<=m;i++) printf("-1\n"); return 0; } for(int i=1;i<=m;i++) { read(u[i]); read(v[i]); read(len[i]); edge[i]=newtree(len[i],0); } for(int i=1;i<=n;i++) ro[i]=newtree(0,1); int biu; js=n; for(biu=1;biu<=m;++biu) { unite(biu,u[biu],v[biu]); if(!js) break; printf("-1\n"); } if(js) return 0; sora(); tu=q.top(); printf("%d\n",tu.len); for(register int i=biu+1;i<=m;++i) { unite(i,u[i],v[i]); sora(); tu=q.top(); printf("%d\n",tu.len); } return 0;}
你知道雪为什么是白色的吗,因为她忘了自己曾经的颜色。
- codeforces 603E(LCT+优先队列)
- Codeforces 767E 优先队列
- codeforces 867 E Buy Low Sell High(优先队列)
- codeforces 507E (dijkstra+优先队列)
- codeforces 731E (优先队列 DP)
- CodeForces 732E - Sockets(优先队列)
- CodeForces 825E Educational #25 E:拓扑排序+优先队列
- Codeforces #669 Div2 E. Little Artem and Time Machine(离线、优先队列)
- Codeforces Round #437 (Div. 2)E. Buy Low Sell High(优先队列)
- CodeForces 767E Change-free【贪心+优先队列】
- Codeforces 591E Three States【优先队列Bfs+思维】
- Codeforces E. President and Roads (优先队列Dijkstar + 强连通 找必最短路上的必须边(桥))经典
- Codeforces E. President and Roads (优先队列Dijkstar + 强连通 找必最短路上的必须边(桥))
- codeforces 446B(优先队列)
- Codeforces 721D (优先队列,模拟)
- CodeForces 128 B.String(优先队列)
- 【映射 && 优先队列】CodeForces
- 优先队列 CodeForces
- js再次确认密码
- 注释
- Windows 更新安装 Windows 7 SP1失败错误代码80073712解决方法:
- FP-growth算法发现频繁项集(一)——构建FP树
- Python 中 变量的 is 和 == 符号的作用
- codeforces 603E(LCT+优先队列)
- 链表的常见操作入门(一)
- Codeforces Round #433 (Div. 1, based on Olympiad of Metropolises) C. Boredom(主席树)
- OpenGL2.0 和 OpenGL3.0 的不同与共同点
- JSP知识点汇总(一)
- 负载均衡
- zookeeper注册服务信息---获取IP地址及空闲端口(nodejs typescrip)
- 使用AppBarLayout+CollapsingToolbarLayout实现自定义工具栏折叠效果
- 对抗自编码