BZOJ 3669 [Noi2014]魔法森林(贪心+LCT)
【题目链接】 http://www.lydsy.com/JudgeOnline/problem.php?id=3669
【题目大意】
给出一张图,每条边上有两个值ai和bi,现在需要求出一条1到N的路,
求使得路上ai的最大值与bi的最大值的和最小。
【题解】
我们按照ai的权值从小到大排序,依次加边,我们只要求出bi最大值最小的生成路即可,
我们加入边的时候如果发现之前两个点是相连的,并且路径中bi最大值比当前加入的边要大,
那么我们把原来大的那条边cut掉,加入现在这条边。
动态树对于边的处理可以将边权转化为点权。
【代码】
#include <cstdio> #include <algorithm> #include <cstring> using namespace std; const int N=200010,INF=0x3f3f3f3f; int f[N],son[N][2],val[N],mx[N],tmp[N],from[N];bool rev[N]; bool isroot(int x){return !f[x]||son[f[x]][0]!=x&&son[f[x]][1]!=x;} void rev1(int x){if(!x)return;swap(son[x][0],son[x][1]);rev[x]^=1;} void pb(int x){if(rev[x])rev1(son[x][0]),rev1(son[x][1]),rev[x]=0;} void up(int x){ mx[x]=val[x],from[x]=x; if(son[x][0])if(mx[son[x][0]]>mx[x])mx[x]=mx[son[x][0]],from[x]=from[son[x][0]]; if(son[x][1])if(mx[son[x][1]]>mx[x])mx[x]=mx[son[x][1]],from[x]=from[son[x][1]]; } void rotate(int x){ int y=f[x],w=son[y][1]==x; son[y][w]=son[x][w^1]; if(son[x][w^1])f[son[x][w^1]]=y; if(f[y]){ int z=f[y]; if(son[z][0]==y)son[z][0]=x;else if(son[z][1]==y)son[z][1]=x; }f[x]=f[y];f[y]=x;son[x][w^1]=y;up(y); } void splay(int x){ int s=1,i=x,y;tmp[1]=i; while(!isroot(i))tmp[++s]=i=f[i]; while(s)pb(tmp[s--]); while(!isroot(x)){ y=f[x]; if(!isroot(y)){if((son[f[y]][0]==y)^(son[y][0]==x))rotate(x);else rotate(y);} rotate(x); }up(x); } void access(int x){for(int y=0;x;y=x,x=f[x])splay(x),son[x][1]=y,up(x);} int root(int x){access(x);splay(x);while(son[x][0])x=son[x][0];return x;} void makeroot(int x){access(x);splay(x);rev1(x);} void link(int x,int y){makeroot(x);f[x]=y;access(x);} void cutf(int x){access(x);splay(x);f[son[x][0]]=0;son[x][0]=0;up(x);} void cut(int x,int y){makeroot(x);cutf(y);} int ask(int x,int y){makeroot(x);access(y);splay(y);return mx[y];} int askfrom(int x,int y){makeroot(x);access(y);splay(y);return from[y];} void init(){ memset(son,0,sizeof(son)); memset(rev,0,sizeof(rev)); memset(f,0,sizeof(f)); memset(val,0,sizeof(val)); } struct edge{int x,y,a,b;}e[N]; bool cmp(edge a,edge b){return a.a<b.a;} int n,m; int main(){ while(~scanf("%d%d",&n,&m)){ init(); int ans=INF; for(int i=1;i<=m;i++)scanf("%d%d%d%d",&e[i].x,&e[i].y,&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,from[n+i]=n+i; for(int i=1;i<=m;i++){ if(root(e[i].x)!=root(e[i].y))link(e[i].x,n+i),link(e[i].y,n+i); else{ int pos=askfrom(e[i].x,e[i].y); if(e[i].b<val[pos]){ cut(pos,e[pos-n].x),cut(pos,e[pos-n].y); link(e[i].x,n+i),link(e[i].y,n+i); } }if(root(1)==root(n))ans=min(ans,e[i].a+ask(1,n)); }if(ans==INF)puts("-1"); else printf("%d\n",ans); }return 0; }
愿你出走半生,归来仍是少年