题目大意:给定一个无向图,一些点有权值,其它点的权值可以自己指定,要求指定这些点的权值,使每条边两边的点权异或值之和最小
在此基础上要求点权和最小
首先不考虑点权和最小这个条件 那么我们将每一位分开计算 我们会发现这是一个最小割的模型
令S集为0,T集为1,如果这个点的点权已经指定,则向相应集合连流量为INF的边
每条边的两端点之间连一条流量为1的边
跑最小割就是答案
现在我们将点权考虑进去
将原图每条边的流量扩大10000倍
如果一个点和S没有连边,就从S向这个点连一条流量为1的边 这样如果和S集割开则需要付出1的代价
这样求出最小割,/10000就是最小边权和,%10000就是最小点权和
每一位处理之后加和即可
#include <cstdio> #include <cstring> #include <iostream> #include <algorithm> #define M 510 #define S 0 #define T (n+1) #define INF 0x3f3f3f3f using namespace std; struct edge{ int x,y; }edges[2015]; int n,m,a[M]; long long ans1,ans2; namespace Max_Flow{ struct abcd{ int to,f,next; }table[100100]; int head[M],tot=1; int dpt[M]; void Add(int x,int y,int z) { table[++tot].to=y; table[tot].f=z; table[tot].next=head[x]; head[x]=tot; } inline void Link(int x,int y,int z) { Add(x,y,z); Add(y,x,z); } void Initialize() { memset(head,0,sizeof head); tot=1; } bool BFS() { static int q[M]; int i,r=0,h=0; memset(dpt,-1,sizeof dpt); dpt[S]=1;q[++r]=S; while(r!=h) { int x=q[++h]; for(i=head[x];i;i=table[i].next) if(table[i].f&&!~dpt[table[i].to]) { dpt[table[i].to]=dpt[x]+1; q[++r]=table[i].to; if(table[i].to==T) return true; } } return false; } int Dinic(int x,int flow) { int i,left=flow; if(x==T) return flow; for(i=head[x];i;i=table[i].next) if(table[i].f&&dpt[table[i].to]==dpt[x]+1) { int temp=Dinic(table[i].to,min(left,table[i].f) ); left-=temp; table[i].f-=temp; table[i^1].f+=temp; } if(left) dpt[x]=-1; return flow-left; } } int main() { using namespace Max_Flow; int i,j; cin>>n>>m; for(i=1;i<=n;i++) scanf("%d",&a[i]); for(i=1;i<=m;i++) scanf("%d%d",&edges[i].x,&edges[i].y); for(j=30;~j;j--) { Initialize(); for(i=1;i<=n;i++) { if(a[i]<0) Link(S,i,1); else if( a[i]&(1<<j) ) Link(S,i,1),Link(i,T,INF); else Link(S,i,INF); } for(i=1;i<=m;i++) Link(edges[i].x,edges[i].y,10000); long long temp=0; while( BFS() ) temp+=Dinic(S,INF); ans1+=static_cast<long long>(temp/10000)<<j; ans2+=static_cast<long long>(temp%10000)<<j; } cout<<ans1<<endl<<ans2<<endl; return 0; }
原文地址:http://blog.csdn.net/popoqqq/article/details/42553611