【BZOJ2400】Spoj 839 Optimal Marks 最小割
【BZOJ2400】Spoj 839 Optimal Marks
Description
定义无向图中的一条边的值为:这条边连接的两个点的值的异或值。
定义一个无向图的值为:这个无向图所有边的值的和。
给你一个有n个结点m条边的无向图。其中的一些点的值是给定的,而其余的点的值由你决定(但要求均为非负数),使得这个无向图的值最小。在无向图的值最小的前提下,使得无向图中所有点的值的和最小。
Input
第一行,两个数n,m,表示图的点数和边数。
接下来n行,每行一个数,按编号给出每个点的值(若为负数则表示这个点的值由你决定,值的绝对值大小不超过10^9)。
接下来m行,每行二个数a,b,表示编号为a与b的两点间连一条边。(保证无重边与自环。)
Output
第一行,一个数,表示无向图的值。
第二行,一个数,表示无向图中所有点的值的和。
Sample Input
3 2
2
-1
0
1 2
2 3
2
-1
0
1 2
2 3
Sample Output
2
2
2
HINT
数据约定
n<=500,m<=2000
样例解释
2结点的值定为0即可。
题解:先拆位,然后每个数要么是0要么是1,这显然就转换成了一个最小割问题。
只考虑所有-1的点i,从S->i连一条边代表i有多少1与i相连,从i->T连一条边代表有多少0与i相连,从i->j连一条边代表i,j不同所付出的代价。
但是要求点权和最小怎么办?将边权*10000+点权即可。
#include <cstdio> #include <cstring> #include <iostream> #include <queue> using namespace std; typedef long long ll; int n,m,ans,S,T,cnt; ll ans1,ans2; int pa[2010],pb[2010],v[510],d[510],head[510],next[10010],val[10010],to[10010],s1[510],s2[510]; queue<int> q; int rd() { int ret=0,f=1; char gc=getchar(); while(gc<'0'||gc>'9') {if(gc=='-') f=-f; gc=getchar();} while(gc>='0'&&gc<='9') ret=ret*10+gc-'0',gc=getchar(); return ret*f; } void add(int a,int b,int c) { to[cnt]=b,val[cnt]=c,next[cnt]=head[a],head[a]=cnt++; to[cnt]=a,val[cnt]=0,next[cnt]=head[b],head[b]=cnt++; } int dfs(int x,int mf) { if(x==T) return mf; int i,k,temp=mf; for(i=head[x];i!=-1;i=next[i]) { if(d[to[i]]==d[x]+1&&val[i]) { k=dfs(to[i],min(temp,val[i])); if(!k) d[to[i]]=0; val[i]-=k,val[i^1]+=k,temp-=k; if(!temp) break; } } return mf-temp; } int bfs() { memset(d,0,sizeof(d)); while(!q.empty()) q.pop(); int i,u; q.push(S),d[S]=1; while(!q.empty()) { u=q.front(),q.pop(); for(i=head[u];i!=-1;i=next[i]) { if(!d[to[i]]&&val[i]) { d[to[i]]=d[u]+1; if(to[i]==T) return 1; q.push(to[i]); } } } return 0; } void work(int x) { int i; S=0,T=n+1; memset(head,-1,sizeof(head)),cnt=ans=0; memset(s1,0,sizeof(s1)),memset(s2,0,sizeof(s2)); for(i=1;i<=n;i++) { if(v[i]>=0) ans2+=(v[i]&x); else s2[i]=1; } for(i=1;i<=m;i++) { if(v[pa[i]]>=0) { ans1+=((v[pa[i]]&x)^(v[pb[i]]&x)); } else if(v[pb[i]]>=0) { if(v[pb[i]]&x) s1[pa[i]]+=10000; else s2[pa[i]]+=10000; } else add(pa[i],pb[i],10000),add(pb[i],pa[i],10000); } for(i=1;i<=n;i++) if(v[i]<0) add(S,i,s1[i]),add(i,T,s2[i]); while(bfs()) ans+=dfs(S,1<<30); ans1+=(ll)x*(ans/10000),ans2+=(ll)x*(ans%10000); } int main() { n=rd(),m=rd(); int i; for(i=1;i<=n;i++) v[i]=rd(); for(i=1;i<=m;i++) { pa[i]=rd(),pb[i]=rd(); if(v[pa[i]]>v[pb[i]]) swap(pa[i],pb[i]); } for(i=0;i<=30;i++) work(1<<i); printf("%lld\n%lld",ans1,ans2); return 0; } //3 2 3 -1 2 1 2 2 3
| 欢迎来原网站坐坐! >原文链接<