【uoj3】 NOI2014—魔法森林
http://uoj.ac/problem/3 (题目链接)
题意
给出一张带权图,每条边有两个权值A和B,一条路径的花费为路径中的最大的A和最大的B之和。求从1走到n的最小花费。
Solution
枚举A,SPFA松弛。
不得不说UOJ的hack还是很强力的,仔细想了想,数据好像也并不是特别难构。只要使每次加边都必须遍历整张图,之前n都未连通,最后一条A最大的边连向n即可。
代码
// uoj3 #include<algorithm> #include<iostream> #include<cstdlib> #include<cstring> #include<cstdio> #include<vector> #include<cmath> #include<queue> #include<ctime> #define LL long long #define inf (1ll<<30) #define Pi acos(-1.0) #define free(a) freopen(a".in","r",stdin),freopen(a".out","w",stdout); using namespace std; const int maxn=50010,maxm=100010; int head[maxn],dis[maxn],fa[maxn],vis[maxn],n,m,cnt; struct data {int u,v,wa,wb;}a[maxm]; struct edge {int to,next,wa,wb;}e[maxm<<1]; void link(int u,int v,int wa,int wb) { e[++cnt]=(edge){v,head[u],wa,wb};head[u]=cnt; e[++cnt]=(edge){u,head[v],wa,wb};head[v]=cnt; } int find(int x) { return x==fa[x] ? x : fa[x]=find(fa[x]); } bool cmp(data a,data b) { return a.wa<b.wa; } int main() { scanf("%d%d",&n,&m); for (int i=1;i<=n;i++) fa[i]=i; for (int i=1;i<=m;i++) { scanf("%d%d%d%d",&a[i].u,&a[i].v,&a[i].wa,&a[i].wb); link(a[i].u,a[i].v,a[i].wa,a[i].wb); if (find(a[i].u)!=find(a[i].v)) fa[find(a[i].u)]=find(a[i].v); } if (find(1)!=find(n)) {puts("-1");return 0;} sort(a+1,a+1+m,cmp); for (int i=1;i<=n;i++) dis[i]=inf;dis[1]=0; int ans=inf;vis[n]=1; for (int i=1;i<=m;i++) { queue<int> q; if (!vis[a[i].u]) q.push(a[i].u),vis[a[i].u]=1; if (!vis[a[i].v]) q.push(a[i].v),vis[a[i].v]=1; while (i<m && a[i+1].wa==a[i].wa) { i++; if (!vis[a[i].u]) q.push(a[i].u),vis[a[i].u]=1; if (!vis[a[i].v]) q.push(a[i].v),vis[a[i].v]=1; } while (!q.empty()) { int x=q.front();q.pop(); vis[x]=0; for (int j=head[x];j;j=e[j].next) if (e[j].wa<=a[i].wa && dis[e[j].to]>max(dis[x],e[j].wb)) { dis[e[j].to]=max(dis[x],e[j].wb); if (!vis[e[j].to]) vis[e[j].to]=1,q.push(e[j].to); } } ans=min(ans,a[i].wa+dis[n]); if ((double)clock()/CLOCKS_PER_SEC>=2.8) break; } printf("%d\n",ans); return 0; }
Solution
link cut tree维护最小生成树。把边权转为点权表示。每次加边时,如果${u,v}$在一个连通块内,查询连通块中点权最大值,如果大于当前边的边权,就把这个点删掉。每次加完一条边后,如果起点和终点在同一连通块中,就更新下答案。
代码
// uoj2 #include<algorithm> #include<iostream> #include<cstdlib> #include<cstring> #include<cstdio> #include<cmath> #define LL long long #define inf (1ll<<30) #define Pi acos(-1.0) #define free(a) freopen(a".in","r",stdin),freopen(a".out","w",stdout); using namespace std; const int maxn=200010; int a[maxn],tr[maxn][2],rev[maxn],fa[maxn],mx[maxn]; int n,m; struct edge { int u,v,wa,wb; friend bool operator < (const edge a,const edge b) { return a.wa<b.wa; } }e[maxn]; void pushup(int x) { if (a[mx[tr[x][0]]]>a[mx[tr[x][1]]]) mx[x]=mx[tr[x][0]]; else mx[x]=mx[tr[x][1]]; if (a[mx[x]]<a[x]) mx[x]=x; } void pushdown(int x) { if (tr[fa[x]][0]==x || tr[fa[x]][1]==x) pushdown(fa[x]); if (rev[x]) { swap(tr[x][0],tr[x][1]); rev[tr[x][0]]^=1;rev[tr[x][1]]^=1;rev[x]^=1; } } void rotate(int x) { int y=fa[x],z=fa[y],l,r; l=tr[y][1]==x;r=l^1; if (tr[z][0]==y || tr[z][1]==y) tr[z][tr[z][1]==y]=x; fa[y]=x;fa[x]=z;fa[tr[x][r]]=y; tr[y][l]=tr[x][r];tr[x][r]=y; pushup(y);pushup(x); } void splay(int x) { pushdown(x); while (tr[fa[x]][0]==x || tr[fa[x]][1]==x) { int y=fa[x],z=fa[y]; if (tr[z][0]==y || tr[z][1]==y) { if (tr[z][0]==y ^ tr[y][0]==x) rotate(x); else rotate(y); } rotate(x); } } void access(int x) { for (int y=0;x;y=x,x=fa[x]) splay(x),tr[x][1]=y,pushup(x); } void makeroot(int x) { access(x);splay(x);rev[x]^=1; } void link(int x,int y) { makeroot(x);fa[x]=y; } void cut(int x,int y) { makeroot(x);access(y);splay(y); tr[y][0]=fa[x]=0;pushup(y); } int find(int x) { while (fa[x]) x=fa[x]; return x; } int query(int x,int y) { makeroot(x);access(y);splay(y); return mx[y]; } int main() { scanf("%d%d",&n,&m); for (int i=1;i<=m;i++) scanf("%d%d%d%d",&e[i].u,&e[i].v,&e[i].wa,&e[i].wb); sort(e+1,e+1+m); int ans=inf; for (int i=1;i<=n;i++) a[i]=0; for (int i=1;i<=m;i++) { a[i+n]=e[i].wb; if (find(e[i].u)!=find(e[i].v)) link(e[i].u,i+n),link(e[i].v,i+n); else { int tmp=query(e[i].u,e[i].v); if (a[tmp]>a[i+n]) { cut(e[tmp-n].u,tmp),cut(e[tmp-n].v,tmp); link(e[i].u,i+n),link(e[i].v,i+n); } } if (find(1)==find(n)) ans=min(ans,e[i].wa+a[query(1,n)]); } printf("%d",ans==inf ? -1 : ans); return 0; }
This passage is made by MashiroSky.