CodeForces - 76A:Gift (最小生成树 解决单调性问题是思想)
题意:给定N点M边的无向连通图,每条边有两个权值(g,s)。 给定G,S。 让你给出一组(g0,s0)使得图中仅留下g<=g0, s<=s0的边之后,依然连通,并求Gg0+Ss0的最小值。 n<=200,m<=50000。
思路:枚举g0,求最小的s0,满足生成MST。 把边按g排序,一条边一条边的加入,然后在加入边的集合里面找出最小的s。但是每次排序复杂度过高,而且边数也过多,(LCT做也行吧,就不需要考虑这么多)。 我们去优化暴力的做法。假设新加入一条边,显然最多改变一条边,那么我们维护一个有序序列,表示MST用的边(N-1条),新加入后,手动排序(O(N)),然后把这N条边建立新的MST,就可以了。
#include<bits/stdc++.h> #define ll long long #define pair<ll,ll> pii #define x first #define y second #define rep(i,a,b) for(int i=a;i<=b;i++) using namespace std; const int maxn=200010; struct in{ int u,v;ll a,b; }s[maxn]; bool cmp(in p,in q){ if(p.a!=q.a) return p.a<q.a; return p.b<q.b; } int fa[maxn],q[maxn]; int find(int x){ if(x==fa[x]) return x; return fa[x]=find(fa[x]); } int main() { int N,M,tot=0; ll G,S,A=0,B=0,ans=-1; scanf("%d%d%lld%lld",&N,&M,&G,&S); rep(i,1,M){ scanf("%d%d%lld%lld",&s[i].u,&s[i].v,&s[i].a,&s[i].b); if(s[i].u==s[i].v) i--,M--; } sort(s+1,s+M+1,cmp); rep(i,1,M){ q[++tot]=i; int t=tot; while(t>1&&s[q[t]].b<s[q[t-1]].b) swap(q[t],q[t-1]),t--; rep(j,1,N) fa[j]=j; tot=0; rep(j,1,N) { int fu=find(s[q[j]].u); int fv=find(s[q[j]].v); if(fu==fv) continue; fa[fu]=fv; q[++tot]=q[j]; if(tot==N-1) break; } if(tot==N-1){ if(ans==-1) ans=G*s[i].a+S*s[q[N-1]].b; else ans=min(ans,G*s[i].a+S*s[q[N-1]].b); } } printf("%lld\n",ans); return 0; }
It is your time to fight!