BZOJ 2400(网络流最小割)
2016-03-03 19:50
423 查看
思路:
首先比较显然能够想到二进制拆分
每一位的贡献是独立的
然后可以用最小割解决
S->i 表示这一个位置是0 inf
i-T 表示为1 inf
然后所有的边保留 1
这样只要最后再找一遍t集合里面的点就可以确定那些不确定的点了
第二问好像没什么用? 确定了每个位置后他的值也就确定了
10^9的限制是干嘛的?
代码
首先比较显然能够想到二进制拆分
每一位的贡献是独立的
然后可以用最小割解决
S->i 表示这一个位置是0 inf
i-T 表示为1 inf
然后所有的边保留 1
这样只要最后再找一遍t集合里面的点就可以确定那些不确定的点了
第二问好像没什么用? 确定了每个位置后他的值也就确定了
10^9的限制是干嘛的?
代码
#include<iostream> #include<cstdio> #include<cstdlib> #include<algorithm> #include<cmath> #include<cstring> #include<string> #include<queue> #include<vector> using namespace std; typedef long long LL; const int imax=500+9; const int dmax=imax; const int bmax=100000+229; const int inf=100000229; int n,m; int a[imax],A[bmax],B[bmax],val[dmax],ind[dmax]; int S,T,num,head[dmax],inext[bmax],to[bmax],re[bmax]; void iread() { scanf("%d%d",&n,&m); for(int i=1;i<=n;i++) scanf("%d",&a[i]); for(int i=1;i<=m;i++) scanf("%d%d",&A[i],&B[i]); } void findset(int x) { ind[x]=1; for(int i=head[x];i!=-1;i=inext[i]) if(!ind[to[i]] && re[i^1]) findset(to[i]); //反向边有流量说明这条边有贡献 } void iadd(int u,int v,int flow){ to[num]=v; re[num]=flow; inext[num]=head[u]; head[u]=num++;} void add(int u,int v,int flow) { iadd(u,v,flow); iadd(v,u,0); } int d[dmax]; queue<int> q; bool BFS() { memset(d,0,sizeof(d)); d[S]=1; q.push(S); while(!q.empty()) { int u=q.front(); q.pop(); for(int i=head[u];i!=-1;i=inext[i]) if(re[i] && !d[to[i]]) { d[to[i]]=d[u]+1; q.push(to[i]); } } return d[T]!=0; } int DFS(int x,int c) { if(x==T || c==0) return c; int r=c; for(int i=head[x];i!=-1;i=inext[i]) if(d[to[i]]==d[x]+1) { int ff=DFS(to[i],min(re[i],r)); r-=ff; re[i]-=ff; re[i^1]+=ff; if(!r) break; } if(r==c) d[x]=0; return c-r; } int dinic() { int nowans=0; while(BFS()) nowans+=DFS(S,inf); // printf("%d\n",nowans); return nowans; } void build(int k) { S=0; T=n+1; num=0; for(int i=S;i<=T;i++) head[i]=-1; for(int i=1;i<=n;i++) if(a[i]>=0) { if(a[i]&(1<<(k-1))) add(i,T,inf); else add(S,i,inf); } for(int i=1;i<=m;i++) { add(A[i],B[i],1); add(B[i],A[i],1); } } void iwork() { int Max=30; LL ans1=0; LL ans2=0; for(int i=1;i<=Max;i++) { build(i); ans1+=(1LL<<(i-1))*dinic(); memset(ind,false,sizeof(ind)); findset(T); for(int j=1;j<=n;j++) if(ind[j]) val[j]+=(1<<(i-1)); } for(int i=1;i<=n;i++) { if(a[i]>0) ans2+=a[i]; else ans2+=val[i]; } cout<<ans1<<endl<<ans2<<endl; } int main() { iread(); iwork(); return 0; }
相关文章推荐
- mysql binlog二进制日志详解
- 详解C++编程中对二进制文件的读写操作
- 整理C# 二进制,十进制,十六进制 互转
- c#二进制逆序方法详解
- JS幻想 读取二进制文件第1/2页
- 使用jscript实现二进制读写脚本代码
- C#二进制序列化实例分析
- JavaScript前端开发之实现二进制读写操作
- PHP函数篇详解十进制、二进制、八进制和十六进制转换函数说明
- javascript 二进制运算技巧解析
- 如何判断一个整数的二进制中有多少个1
- MSSQL 将截断字符串或二进制数据问题的解决方法
- 二进制中1的个数
- C++ 十进制转换为二进制的实例代码
- C语言十进制转二进制代码实例
- asp.net实现图片以二进制流输出的两种方法
- C++二进制翻转实例分析
- PowerShell把IP地址转换成二进制的方法
- 科学知识:二进制、八进制、十进制、十六进制转换
- C#读取二进制文件方法分析