bzoj3669[Noi2014]魔法森林
Posted oyzx~
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了bzoj3669[Noi2014]魔法森林相关的知识,希望对你有一定的参考价值。
#include <iostream> #include <cstdio> #include <cstring> #include <cmath> #include <algorithm> #define maxn 150005 #define maxm 100005 #define pi pair<int,int> #define mp(a,b) make_pair(a,b) using namespace std; struct note{ int u,v,a,b; }wi[maxm]; int n,m,ans,fa[maxn],son[maxn][2],val[maxn],sm[maxn],sm_id[maxn]; bool rev[maxn]; bool comp(note x,note y){ if (x.a==y.a) return x.b<y.b; return x.a<y.a; } struct date{ int which(int x){ return son[fa[x]][1]==x; } int isroot(int x){ return son[fa[x]][1]!=x&&son[fa[x]][0]!=x; } void update(int x){ sm_id[x]=x,sm[x]=val[x]; if (son[x][0]&&sm[son[x][0]]>sm[x]) sm[x]=sm[son[x][0]],sm_id[x]=sm_id[son[x][0]]; if (son[x][1]&&sm[son[x][1]]>sm[x]) sm[x]=sm[son[x][1]],sm_id[x]=sm_id[son[x][1]]; } void pushdown(int x){ if (rev[x]){ rev[x]^=1,swap(son[x][1],son[x][0]); if (son[x][0]) rev[son[x][0]]^=1; if (son[x][1]) rev[son[x][1]]^=1; } } void relax(int x){ if (!isroot(x)) relax(fa[x]); pushdown(x); } void rotata(int x){ int y=fa[x],d=which(x),dd=which(y); if (!isroot(y)) son[fa[y]][dd]=x; fa[x]=fa[y]; fa[son[x][d^1]]=y,son[y][d]=son[x][d^1]; fa[y]=x,son[x][d^1]=y; update(y); } void splay(int x){ relax(x); while (!isroot(x)){ if (isroot(fa[x])) rotata(x); else if (which(x)==which(fa[x])) rotata(fa[x]),rotata(x); else rotata(x),rotata(x); } update(x); } void access(int x){ for (int p=0;x;x=fa[x]){ splay(x); son[x][1]=p; update(x); p=x; } } void make_root(int x){ access(x); splay(x); rev[x]^=1; } void link(int x,int y){ make_root(x); fa[x]=y; } void cut(int x,int y){ make_root(x); access(y); splay(y); son[y][0]=fa[x]=0; update(y); } void split(int x,int y){ make_root(x); access(y); splay(y); } int query(int x,int y){ split(x,y); return sm[y]; } pi find(int x,int y){ split(x,y); return mp(sm_id[y],sm[y]); } int find_root(int x){ access(x); splay(x); while (son[x][0]) x=son[x][0]; return x; } }lct; int main(){ // freopen("forest.in","r",stdin); // freopen("forest.out","w",stdout); memset(rev,0,sizeof(rev)); memset(fa,0,sizeof(fa)); memset(son,0,sizeof(son)); memset(val,0,sizeof(val)); memset(sm,0,sizeof(sm)); scanf("%d%d",&n,&m); for (int i=1;i<=m;i++) scanf("%d%d%d%d",&wi[i].u,&wi[i].v,&wi[i].a,&wi[i].b); sort(wi+1,wi+m+1,comp); for (int i=1;i<=n;i++) val[i]=0,lct.update(i); ans=maxn; pi temp; for (int i=1;i<=m;i++){ int u=wi[i].u,v=wi[i].v; if (lct.find_root(u)!=lct.find_root(v)){ val[n+i]=wi[i].b,lct.update(n+i); lct.link(n+i,u),lct.link(n+i,v); }else{ temp=lct.find(u,v); if (temp.second<=wi[i].b) continue; else{ int t=temp.first; lct.cut(wi[t-n].u,t),lct.cut(wi[t-n].v,t); val[n+i]=wi[i].b,lct.update(n+i); lct.link(n+i,u),lct.link(n+i,v); } } if (lct.find_root(1)!=lct.find_root(n)) continue; int t=lct.query(1,n); ans=min(ans,t+wi[i].a); } if (ans>maxm) printf("-1\\n"); else printf("%d\\n",ans); return 0; }
题目链接:http://www.lydsy.com/JudgeOnline/problem.php?id=3669
题目大意:给定一个无向图,每条边有两个权值va,vb,要求选一条从1到n的路径,满足这条路径上点的max(va)+max(vb)最小,若没有从A到B的路径,则输出-1。
做法:初看这题,暴力写法:将边按va升序排序,枚举i,此时max(va)=vi,保证max(vb)最小即可,我们可以想到kruscal,并查集集维护即可,但是瓶颈在于每次都要将1~i的边按vb升序排序,在O(n)的加入,这种做法复杂度过高,不宜使用。
仔细想想:我们可以考虑用lct维护这个过程,考虑先将边按va升序排序,然后依次加入每一条边,此时max(va)=vi,保证max(vb)最小即可,加入该边时会有两种情况:
1.不形成环,则加入这条边,若节点1与节点n联通,则用1到n链上vb最大值+vi更新答案,否则不更新答案。
2.形成环,与lct模拟kruscal的过程一样,删掉原本那条链上vb权值最大的边,并加入这条边,若节点1与节点n联通,则用1到n链上vb最大值+vi更新答案,否则不更新答案。
lct+离线处理
以上是关于bzoj3669[Noi2014]魔法森林的主要内容,如果未能解决你的问题,请参考以下文章