思路
既然是异或预算,很容易想到按位操作。
按位操作之后,每个点的权值就只有\(0\)和\(1\)两个了,然后从\(S\)向所有权值为\(0\)的点连一条\(INF\)的边,从所有权值为\(1\)的点向\(T\)连一条\(INF\)的边。然后将原图中的边全都连成权值为\(1\)的边。然后求最小割。
如果没有不确定的点权,那么很明显这样是对的。
画出图来就可以知道,对于不确定的点权这样操作也是对的。
这样就可以完成第一问了。
然后考虑第二问。在满足最小割的情况下要求点权最小。也就是说。再使得最小割不变的情况下,每个点的权值尽量的为\(0\),也就是说尽量的割掉与\(T\)的连边。这样就有一个非常神奇的做法。
将原图中的边不再连\(1\),而是连成\(10000\).然后从\(S\)向每个点连一条权值为\(1\)的边。画出图来就可以发现这样是对的。
因为如果当前割掉与\(S\)的连边和割掉与\(T\)的连边同样优秀的话,如果割掉了与\(S\)的连边,那么还有一条与\(S\)相连为\(1\)的边。使得还有\(1\)的流量,肯定会不优秀。所以这种情况就会优先割掉与T的连边。
代码
/*
* @Author: wxyww
* @Date: 2019-02-09 11:35:38
* @Last Modified time: 2019-02-10 07:53:05
*/
#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;
typedef long long ll;
#define int ll
const int N = 2010,INF = 1e9;
ll read() {
ll x=0,f=1;char c=getchar();
while(c<'0'||c>'9') {
if(c=='-') f=-1;
c=getchar();
}
while(c>='0'&&c<='9') {
x=x*10+c-'0';
c=getchar();
}
return x*f;
}
struct node {
int v,nxt,w,u;
}e[N * 10];
int head[N],ejs;
void add(int u,int v,int w) {
e[++ejs].v = v;e[ejs].nxt = head[u];head[u] = ejs;e[ejs].w = w;
e[++ejs].v = u;e[ejs].nxt = head[v];head[v] = ejs;e[ejs].w = 0;
}
int n,m,w[N],U[N],V[N];
int dep[N],S,T;
queueq;
int bfs() {
memset(dep,0,sizeof(dep));
while(!q.empty()) q.pop();
dep[S] = 1;
q.push(S);
while(!q.empty()) {
int u = q.front();q.pop();
for(int i = head[u];i;i = e[i].nxt) {
int v = e[i].v;
if(!dep[v] && e[i].w) {
dep[v] = dep[u] + 1;
q.push(v);
if(v == T) return 1;
}
}
}
return 0;
}
int dfs(int u,int now) {
if(u == T) return now;
int ret = 0;
for(int i = head[u];i;i = e[i].nxt) {
int v = e[i].v;
if(e[i].w && dep[v] == dep[u] + 1) {
int k = dfs(v,min(now - ret,e[i].w));
e[i].w -= k;
e[i ^ 1].w += k;
ret += k;
if(ret == now) return now;
}
}
return ret;
}
int dinic() {
int ans = 0;
while(bfs()) ans += dfs(S,INF);
return ans;
}
void build(int x) {
ejs = 1;
memset(head,0,sizeof(head));
for(int i = 1;i <= n;++i) {
if(w[i] >= 0) {
if(w[i] & x) add(i,T,INF),add(S,i,1);
else add(S,i,INF);
}
else add(S,i,1);
}
for(int i = 1;i <= m;++i) {
add(U[i],V[i],10000);add(V[i],U[i],10000);
}
}
signed main() {
n = read();m = read();
S = n + 1,T = S + 1;
for(int i = 1;i <= n;++i) w[i] = read();
for(int i = 1;i <= m;++i) U[i] = read(),V[i] = read();
int ans1 = 0,ans2 = 0;
for(int i = 0;i <= 31;++i) {
build(1 << i);
int K = dinic();
ans1 += K / 10000 * (1 << i);
ans2 += K % 10000 * (1 << i);
}
printf("%lld\n%lld\n",ans1,ans2);
return 0;
}