BZOJ 3669 【NOI 2014】 魔法森林
题意:
给定一个n个结点,m条边的的无向图,每条边有两个权值ai,bi。 现在从1出发,要到达n,出发时带上任意多的A,B,每次只能沿着ai≤A且bi≤B的边走,问至少要带多少的A,B使得能从1到n。n≤50000,m≤200000。
分析:
二维参数的一些问题,我们经常用的套路是一维排序,另一维数据结构维护,这道题也可用这种手段解决~
问题要从简单处入手→ 假如只有一维参数A(先不考虑B)应该怎么办?
很显然,我们应该用一个Kruskal的思想,因为连通两个点,使最大的边权最小,那么这个最大的边权
一定存在于最小生成树上。
可是现在有两位参数→ 我们怎样应用这种思想呢?
一个思路是,并查集维护连通性,先把边按照A从小到大加入,加入的同时,根据点1和点n间是否连通,
我们分成两种情况:
1. 两个点不连通,那么我们直接将枚举的这条边连通。
2. 如果两个点连通,那我们首先要看这条边是否适合被加入,(假设这条边两端点为x,y)如果想x,y
两点之间路径上的B最大的边的B都比这条新边的B要小,那它显然不适合被加入,因为在两个参数都更
劣的情况下,这条边显然是没有任何竞争力的,但如果原路径上B最大的边的B值比新边大,那么我们就
把B值最大的那条边断掉,并且加入新边。
如此这般,我们每次加边之后,若点1和点n连通,就用当前枚举到的A值和1-n路径上的最大B值加起来去更新答案。
以上步骤,需要用并查集维护连通性,并且用LCT维护每条边的B权值,以此来维护两点间路径上的最大B值。
维护点权的LCT,依然是把“边和权”的形式转化为“边——点权——边”的形式,正常的点权值为零即可!
代码:
1 #include<bits/stdc++.h> 2 #define max(a,b,c) max(max(a,b),c) 3 #define lc(x) t[x][0] 4 #define rc(x) t[x][1] 5 using namespace std; 6 const int N=200005; 7 struct node{int x,y,a,b;}e[N]; 8 int t[N][2],rev[N],val[N],mx[N],fu[N]; 9 int s[N],tp=0,n,m,k,fa[N]; 10 char readchar(){ 11 static char buf[100000],*l=buf,*r=buf; 12 if(l==r) r=(l=buf)+fread(buf,1,100000,stdin); 13 if(l==r) return EOF;return *l++;} 14 int read(){ 15 int x=0,f=1;char ch=readchar(); 16 while(ch<'0'||ch>'9'){if(ch=='-') f=-f;ch=readchar();} 17 while(ch<='9'&&ch>='0'){x=(x<<3)+(x<<1)+ch-'0';ch=readchar();}; 18 return x*f; 19 } char pbuf[100000],*pp=pbuf; 20 void push(const char c) { 21 if(pp-pbuf==100000) fwrite(pbuf,1,100000,stdout),pp=pbuf; 22 *pp++=c; 23 } void write(int x) { 24 static int sta[35]; 25 int top=0; 26 do{sta[top++]=x%10,x/=10;}while(x); 27 while(top) push(sta[--top]+'0');push('\n'); 28 } bool cmp(node u,node v){ 29 return u.a<v.a; 30 } int get(int x){ 31 return fa[x]==x?x:fa[x]=get(fa[x]); 32 } void pushup(int x){ 33 int ls=lc(x),rs=rc(x);mx[x]=x; 34 if(val[mx[ls]]>val[mx[x]]) mx[x]=mx[ls]; 35 if(val[mx[rs]]>val[mx[x]]) mx[x]=mx[rs]; 36 } void pushdown(int x){ 37 int ls=lc(x),rs=rc(x); 38 if(rev[x]) rev[ls]^=1,rev[rs]^=1, 39 swap(lc(x),rc(x)),rev[x]=0;return; 40 } bool pdrt(int x){ 41 return lc(fu[x])!=x&&rc(fu[x])!=x; 42 } void rotate(int x){ 43 int y=fu[x];int z=fu[y]; 44 int dy=(rc(y)==x),dz=(rc(z)==y); 45 if(!pdrt(y)) t[z][dz]=x; 46 t[y][dy]=t[x][dy^1],fu[t[x][dy^1]]=y; 47 t[x][dy^1]=y;fu[y]=x;fu[x]=z; 48 pushup(y); 49 } void splay(int x){ 50 s[++tp]=x; 51 for(int i=x;!pdrt(i);i=fu[i]) 52 s[++tp]=fu[i];while(tp) 53 pushdown(s[tp--]); 54 while(!pdrt(x)){ 55 int y=fu[x];int z=fu[y];if(!pdrt(y)) 56 if(rc(y)==x^rc(z)==y) rotate(x); 57 else rotate(y);rotate(x); 58 } pushup(x); 59 } void access(int x){ 60 for(int i=0;x;x=fu[x]) 61 splay(x),rc(x)=i,i=x; 62 } void mkrt(int x){ 63 access(x),splay(x);rev[x]^=1; 64 } int fdrt(int x){ 65 access(x);splay(x); 66 while(lc(x)) pushdown(x),x=lc(x); 67 return x; 68 } void split(int x,int y){ 69 mkrt(x);access(y);splay(y); 70 } void link(int x,int y){ 71 mkrt(x);if(fdrt(y)!=x) fu[x]=y; 72 } void cut(int x,int y){ 73 mkrt(x); 74 if(fdrt(y)==x&&fu[x]==y&&!rc(x)) 75 fu[x]=t[y][0]=0;pushup(y); 76 } int ask(int x,int y){ 77 split(x,y);return mx[y]; 78 } int main(){ int flg=1,ans=1e9; 79 n=read();m=read(); 80 for(int i=1,x,y,a,b;i<=m;i++) 81 x=read(),y=read(),a=read(),b=read(), 82 e[i]=(node){x,y,a,b};sort(e+1,e+m+1,cmp); 83 for(int i=1;i<=m;i++) 84 val[i+n]=e[i].b,mx[i+n]=i+n; 85 for(int i=1;i<=n;i++) fa[i]=i; 86 for(int i=1;i<=m;i++){ 87 int x=e[i].x,y=e[i].y;int fx=get(x),fy=get(y); 88 if(fx!=fy) link(x,i+n), 89 link(y,i+n),fa[fx]=fy; 90 else{ 91 int p=ask(x,y); 92 if(val[p]>e[i].b) cut(e[p-n].x,p), 93 cut(e[p-n].y,p),link(x,i+n),link(y,i+n); 94 } if(get(1)==get(n)){ 95 flg=0;int p=ask(1,n); 96 ans=min(ans,val[p]+e[i].a); 97 } 98 } if(flg) push('-'),write(1);else write(ans); 99 fwrite(pbuf,1,pp-pbuf,stdout); return 0; 100 }