很有意思的一道题,看到网上有先跑最小割然后dfs的做法,但是还看到一个比较有趣的建图方法。
首先对于题目来说,显然可以因为每一个位之间都不会相互影响所以可以按位处理,枚举每一位然后把每一个点划分为0或者1,这样一来就相当于把每一个元素划分为两个集合,并且要求代价最小,这样可以想到最小割。
然后对于这一道题目要求在保证边权最小的情况下点权和最小,这就相当于要在保证第一条件最优下次要条件也最优,这样我们在网络流中把第一条件的每条边权都扩大10000倍这样次要条件就不会影响到第一条件了(这种思想其实以前遇到过一次)
具体的:
次要条件:s代表0集合,t代表1集合,如果这个点已经确定为1 s->i边权为1 i->t边权为inf这样代表代价必须为1
如果这个点确定为0 s->i边权为inf 不会被割掉
没有确定 s-> 边权为1 可割可不割
第一条件:每一条边对应原图中的边相连边权扩大10000倍
#include<cstdio>
#include<cstring>
#include<iostream>
#define maxn 100021
#define inf 100000000000000ll
#define LL long long
using namespace std;
void read(LL& x){
x=0;char c=getchar();LL f=1;
for(;c>'9'||c<'0';c=getchar())if(c=='-')f=-1;
for(;c>='0'&&c<='9';c=getchar())x=x*10+c-'0';
x*=f;
}
LL n,m,tot=1,head[maxn],last[maxn],q[maxn];
LL h[maxn],s,t,w[maxn],bin[55],ans1,ans2;
struct data{LL a,b;}ed[maxn];
struct edge{LL v,next,w;}e[maxn];
void adde(LL a,LL b,LL c){
e[++tot].v=b;e[tot].next=head[a],e[tot].w=c;
head[a]=tot;
e[++tot].v=a;e[tot].next=head[b],e[tot].w=0;
head[b]=tot;
}
bool bfs(){
for(LL i=0;i<=t;i++)h[i]=-1;
LL l=0,r=1;
q[l]=s,h[s]=0;
while(l<r){
LL u=q[l++];
for(LL v,i=head[u];i;i=e[i].next){
if(h[v=e[i].v]==-1&&e[i].w){
h[v]=h[u]+1;
q[r++]=v;
}
}
}return h[t]!=-1;
}
LL dfs(LL u,LL f){
if(!f||u==t)return f;
LL used=0,w;
for(LL v,i=last[u];i;i=e[i].next){
if(h[v=e[i].v]==h[u]+1&&e[i].w){
last[u]=i;
w=min(f-used,e[i].w);
w=dfs(v,w);
used+=w,e[i].w-=w,e[i^1].w+=w;
if(used==f)return f;
}
}
if(!used)h[u]=-1;
return used;
}
LL dinic(){
LL ans=0;
while(bfs()){
for(LL i=s;i<=t;i++)last[i]=head[i];
ans+=dfs(s,inf);
}return ans;
}
void solve(LL x){
for(LL i=s;i<=t;i++)head[i]=0;
tot=1;
for(LL i=1;i<=n;i++){
if(w[i]<0){
adde(s,i,1);//这一位可以为0或者1代表这条边可割可不割所以与s连边割掉代表选1
}else{
if(w[i]&x)adde(i,t,inf),adde(s,i,1);//必须选一割掉边代价为1
else adde(s,i,inf);
}
}
for(LL i=1;i<=m;i++){
adde(ed[i].a,ed[i].b,10000);
adde(ed[i].b,ed[i].a,10000);
}
LL tmp=dinic();
ans1+=tmp/10000*x;
ans2+=tmp%10000*x;
}
int main(){
read(n),read(m);s=0,t=n+1;
for(LL i=1;i<=n;i++)read(w[i]);
for(LL i=1;i<=m;i++)read(ed[i].a),read(ed[i].b);
bin[0]=1;for(LL i=1;i<=30;i++)bin[i]=bin[i-1]<<1;
for(LL i=30;i>=0;i--)
solve(bin[i]);
printf("%lld\n%lld",ans1,ans2);
return 0;
}