BZOJ3669: [Noi2014]魔法森林
n<=50000,m<=100000的图上,路径有属性Ai和Bi,问从1到n的路径中(Ai的最大值+Bi的最大值)的最小值。
双属性图论题,常用套路--定一动二。比如说这里可以先按A从小到大排序,然后依次加入图中来考虑怎么计算B。
方法一:哦,最大值最小,那不是最小生成树吗?会加边的图,那不是LCT维护最小生成树吗?
1 //#include<iostream> 2 #include<cstring> 3 #include<cstdio> 4 //#include<time.h> 5 //#include<math.h> 6 //#include<set> 7 //#include<queue> 8 //#include<bitset> 9 //#include<vector> 10 #include<algorithm> 11 #include<stdlib.h> 12 using namespace std; 13 14 #define LL long long 15 LL qread() 16 { 17 char c; LL s=0; int f=1; while ((c=getchar())<'0' || c>'9') (c=='-') && (f=-1); 18 do s=s*10+c-'0'; while ((c=getchar())>='0' && c<='9'); return s*f; 19 } 20 21 //Pay attention to '-' , LL and double of qread!!!! 22 23 int n,m; 24 #define maxn 150011 25 struct LCT 26 { 27 struct Node{int son[2],fa,v,Max,maxid; bool rev;}a[maxn]; 28 int n; 29 void clear(int N) {n=N; a[0].Max=a[0].v=-0x3f3f3f3f;} 30 void up(int x) 31 { 32 if (!x) return; 33 Node &b=a[x],&c=a[a[x].son[0]],&d=a[a[x].son[1]]; 34 if (b.v>=c.Max && b.v>=d.Max) {b.Max=b.v; b.maxid=x;} 35 else if (c.Max>=b.v && c.Max>=d.Max) {b.Max=c.Max; b.maxid=c.maxid;} 36 else {b.Max=d.Max; b.maxid=d.maxid;} 37 } 38 void revsingle(int x) {if (!x) return; a[x].rev^=1; swap(a[x].son[0],a[x].son[1]);} 39 void down(int x) {if (a[x].rev) {revsingle(a[x].son[0]); revsingle(a[x].son[1]); a[x].rev=0;} } 40 bool isroot(int x) {return !a[x].fa || (x!=a[a[x].fa].son[0] && x!=a[a[x].fa].son[1]);} 41 void rotate(int x) 42 { 43 int y=a[x].fa,z=a[y].fa; 44 bool w=(x==a[y].son[0]); 45 a[x].fa=z; 46 if (!isroot(y)) a[z].son[y==a[z].son[1]]=x; 47 a[y].son[w^1]=a[x].son[w]; 48 if (a[x].son[w]) a[a[x].son[w]].fa=y; 49 a[x].son[w]=y; 50 a[y].fa=x; 51 up(y); up(z); 52 } 53 int sta[maxn],top; 54 void download(int x) 55 { 56 int i=x; while (!isroot(i)) sta[++top]=i,i=a[i].fa; sta[++top]=i; 57 for (;top;top--) down(sta[top]); 58 } 59 void splay(int x) 60 { 61 if (!x) return; 62 download(x); 63 while (!isroot(x)) 64 { 65 int y=a[x].fa,z=a[y].fa; 66 if (!isroot(y)) {if ((x==a[y].son[0])^(y==a[z].son[0])) rotate(x); else rotate(y);} 67 rotate(x); 68 } 69 up(x); 70 } 71 void access(int x) {int y=0,z=x; while (x) {splay(x); a[x].son[1]=y; up(x); y=x; x=a[x].fa;} splay(z);} 72 void reset(int x) {access(x); revsingle(x);} 73 74 void link(int x,int y) {reset(x); a[x].fa=y;} 75 void cut(int x,int y) 76 {reset(x); access(y); a[x].fa=a[y].son[0]=0; up(y);} 77 bool con(int x,int y) {reset(x); while (y) {if (y==x) return 1; y=a[y].fa;} return 0;} 78 int qmax(int x,int y) {reset(x); access(y); return a[y].maxid;} 79 80 // void test() 81 // { 82 // for (int i=1;i<=n;i++) cout<<i<<" fa"<<a[i].fa<<" son0 "<<a[i].son[0]<<" son1 "<<a[i].son[1] 83 // <<" v"<<a[i].v<<" Max"<<a[i].Max<<" maxid"<<a[i].maxid<<endl; 84 // } 85 }t; 86 87 struct EE{int x,y,a,b; bool operator < (const EE &b) const {return a<b.a;} }ee[maxn]; 88 int main() 89 { 90 n=qread(); m=qread(); 91 for (int i=1;i<=m;i++) {ee[i].x=qread(); ee[i].y=qread(); ee[i].a=qread(); ee[i].b=qread();} 92 sort(ee+1,ee+1+m); 93 94 int ans=0x3f3f3f3f; 95 t.clear(n+m); 96 for (int i=1;i<=n;i++) t.a[i].v=-0x3f3f3f3f,t.a[i].Max=-0x3f3f3f3f,t.a[i].maxid=i; 97 for (int i=n+1;i<=n+m;i++) t.a[i].v=ee[i-n].b,t.a[i].Max=t.a[i].v,t.a[i].maxid=i; 98 for (int i=1;i<=m;i++) 99 { 100 if (t.con(ee[i].x,ee[i].y)) 101 { 102 int z=t.qmax(ee[i].x,ee[i].y); 103 if (t.a[z].v>t.a[i+n].v) 104 { 105 t.cut(ee[z-n].x,z); 106 t.cut(ee[z-n].y,z); 107 t.link(ee[i].x,i+n); 108 t.link(ee[i].y,i+n); 109 } 110 } 111 else {t.link(ee[i].x,i+n); t.link(ee[i].y,i+n); } 112 if (t.con(1,n)) ans=min(ans,ee[i].a+t.a[t.qmax(1,n)].v); 113 } 114 printf("%d\n",ans==0x3f3f3f3f?-1:ans); 115 return 0; 116 }
好久没写了,这次写起来居然这么顺这么短,有点屌。
方法二:LCT学傻了系列,可以先考虑一个暴力:每次加完边之后做一次最短路。不过加了一条边之后,所有导致每个点的答案被更新的根本原因,来自于这条边两端的点的互相更新。也就是说,每加一条边后,前面图的大部分信息是不用算的,就看一下这条边两端的点能否更新彼此,能的话把被更新的点加入队列再跑最短路就OK。复杂度??玄学,反正比LCT快,好写!
1 #include<string.h> 2 #include<stdlib.h> 3 #include<stdio.h> 4 #include<math.h> 5 //#include<assert.h> 6 #include<algorithm> 7 #include<queue> 8 //#include<iostream> 9 //#include<bitset> 10 using namespace std; 11 12 int n,m; 13 #define maxn 200011 14 #define maxm 200011 15 struct List{int from,to,a,b;}list[maxm]; 16 bool cmpa(const List &a,const List &b) {return a.a<b.a;} 17 struct Edge{int to,next,v;}edge[maxm]; int first[maxn],le=2; 18 void in(int x,int y,int v) {Edge &e=edge[le]; e.to=y; e.v=v; e.next=first[x]; first[x]=le++;} 19 void insert(int x,int y,int v) {in(x,y,v); in(y,x,v);} 20 21 struct qnode 22 { 23 int id,v; 24 bool operator > (const qnode &b) const {return v>b.v;} 25 }; 26 priority_queue<qnode,vector<qnode>,greater<qnode> > q; 27 int dis[maxn]; 28 29 void kick(int x,int y,int ev) 30 { 31 if (dis[x]>max(dis[y],ev)) 32 { 33 dis[x]=max(dis[y],ev); 34 q.push((qnode){x,dis[x]}); 35 } 36 } 37 void dijkstra() 38 { 39 while (!q.empty()) 40 { 41 const int now=q.top().id,d=q.top().v; q.pop(); 42 if (dis[now]<d) continue; 43 for (int i=first[now];i;i=edge[i].next) 44 { 45 const Edge &e=edge[i]; 46 if (dis[e.to]>max(dis[now],e.v)) 47 { 48 dis[e.to]=max(dis[now],e.v); 49 q.push((qnode){e.to,dis[e.to]}); 50 } 51 } 52 } 53 } 54 55 int main() 56 { 57 scanf("%d%d",&n,&m); 58 for (int i=1;i<=m;i++) scanf("%d%d%d%d",&list[i].from,&list[i].to,&list[i].a,&list[i].b); 59 sort(list+1,list+1+m,cmpa); 60 list[m+1].a=0x3f3f3f3f; 61 for (int i=1;i<=n;i++) dis[i]=0x3f3f3f3f; 62 dis[1]=0; int ans=0x3f3f3f3f; 63 for (int i=1,j=1;i<=m;i++) if (list[i].a!=list[i+1].a) 64 { 65 for (;j<=i;j++) 66 { 67 insert(list[j].from,list[j].to,list[j].b); 68 kick(list[j].from,list[j].to,list[j].b); 69 kick(list[j].to,list[j].from,list[j].b); 70 } 71 dijkstra(); 72 ans=min(ans,list[i].a+dis[n]); 73 } 74 if (ans==0x3f3f3f3f) puts("-1"); 75 else printf("%d\n",ans); 76 return 0; 77 }