标签:swap play 答案 维护 else php date find \n
魔法森林 bzoj-3669 Noi-2014
题目大意:说不明白题意系列++……题目链接
注释:略。
想法:如果只有1个参量的话spfa、dij什么的都上来了。
两个参量的话我们考虑,想将所有的边按照a排序。
如果两个点:它们之间有两条路径,有一条比另一条劣。
那么我们完全可以将另一条弄掉。
排序之后维护生成树。
LCT的点维护的是实子树中第二参量的最大值。
如果当前边连接的两点之前不连通,直接连上。
如果联通,我们判断新边的第二参量和两点之间splay的最大参量之间的关系。
如果新边的第二参量大,直接舍去。反之,我们用新边替换旧边以得到所有可能答案。
此时,如果1到n联通,直接更新答案。
这个加边删边的过程,我们可以用LCT维护。
两点是否联通,可以用LCT中的find函数实现也可以用并查集维护。
最后,附上丑陋的代码... ...
#include <iostream> #include <cstdio> #include <cstring> #include <algorithm> using namespace std; #define N 150050 #define ls ch[p][0] #define rs ch[p][1] #define get(x) (ch[f[x]][1]==x) int ch[N][2],f[N],rev[N],n,m,val[N],mx[N]; struct Node { int x,y,z,w; }a[N]; inline bool cmp(const Node &x,const Node &y) { return x.z<y.z; } inline bool isroot(int p) { return ch[f[p]][0]!=p&&ch[f[p]][1]!=p; } inline void pushup(int p) { mx[p]=p; if(val[mx[ls]]>val[mx[p]]) mx[p]=mx[ls]; if(val[mx[rs]]>val[mx[p]]) mx[p]=mx[rs]; } inline void pushdown(int p) { if(!rev[p]) return; swap(ch[ls][0],ch[ls][1]); swap(ch[rs][0],ch[rs][1]); rev[ls]^=1; rev[rs]^=1; rev[p]=0; } void update(int p) { if(!isroot(p)) update(f[p]); pushdown(p); } void rotate(int x) { int y=f[x],z=f[y],k=get(x); if(!isroot(y)) ch[z][ch[z][1]==y]=x; ch[y][k]=ch[x][!k]; f[ch[y][k]]=y; ch[x][!k]=y; f[y]=x; f[x]=z; pushup(y); pushup(x); } void splay(int x) { update(x); for(int d;d=f[x],!isroot(x);rotate(x)) if(!isroot(d)) rotate(get(d)==get(x)?d:x); } void access(int p) { int t=0; while(p) splay(p),rs=t,pushup(p),t=p,p=f[p]; } inline void makeroot(int p) { access(p); splay(p); swap(ls,rs); rev[p]^=1; } inline void link(int x,int p) { makeroot(x); splay(p); f[x]=p; } void cut(int x,int p) { makeroot(x); access(p); splay(p); ls=f[x]=0; } int find(int p) { access(p); splay(p); while(ls) pushdown(p),p=ls; return p; } int query(int x,int p) { makeroot(x); access(p); splay(p); return mx[p]; } int main() { scanf("%d%d",&n,&m); int tot=n; for(int i=1;i<=m;i++) { scanf("%d%d%d%d",&a[i].x,&a[i].y,&a[i].z,&a[i].w); } for(int i=1;i<=n;i++) mx[i]=i; sort(a+1,a+m+1,cmp); int ans=1<<30; for(int i=1;i<=m;i++) { int x=a[i].x,y=a[i].y,z=a[i].z,w=a[i].w; int t1=find(x),t2=find(y); tot++; if(t1!=t2) { val[tot]=w; mx[tot]=tot; link(x,tot); link(tot,y); }else { int tmp=query(x,y); if(val[tmp]>w) { cut(a[tmp-n].x,tmp); cut(a[tmp-n].y,tmp); val[tot]=w; mx[tot]=tot; link(x,tot); link(y,tot); } } if(find(1)==find(n)) { ans=min(ans,z+val[query(1,n)]); } } printf("%d\n",ans>100050?-1:ans); }
小结:动态树问题一经转化就只能祭出LCT,别的能维护动态树的我也不会啊...
[bzoj3669][Noi2014]魔法森林_LCT_并查集
标签:swap play 答案 维护 else php date find \n
原文地址:https://www.cnblogs.com/ShuraK/p/9397760.html