题意:
给一张无向图,有些点的点值已经给出,有些点的点值可以自由选择。定义一条边的权值为两点异或值。先要求给这些点赋值,使得最后边的权值和最小,并输出此时的点值和,若有多个方案使得边的权值和最小,那么输出使得点值和最小的方案。
题解:
好题啊。。
首先异或每一位是独立的,可以分开来计算。
发现对于每一位考虑,相当于是给没有确定的点赋0,1值,使得相邻点的两两异或最小。
而原问题其实等价于将点分成S,T集合,不同集合的点之间的连边贡献为1,然后就变成了最小割的模板。
然后就是怎么处理点值和最小了。同样对于残量网络缩点,发现S集合此时所在的强联通是最小点集,直接对原点进行dfs就好了。
#include<bits/stdc++.h>
using namespace std;
inline int rd(){
char ch=getchar();int i=0,f=1;
while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
while(isdigit(ch)){i=(i<<1)+(i<<3)+ch-'0';ch=getchar();}
return i*f;
}
const int Maxn=5e2+50,Maxm=6e3+50;
int n,m,lev[Maxn],cur[Maxm],vis[Maxn],vt,a[Maxn],a2[Maxn],pos;
queue<int>q;
long long ans;
const int src=0,des=501,INF=0x3f3f3f3f;
struct G{
int g[Maxn],v[Maxm],c[Maxm],nt[Maxm],ec;
G(){ec=1;}
inline void add(int x,int y,int o){++ec;nt[ec]=g[x];g[x]=ec;v[ec]=y;c[ec]=o;}
inline void init(){
for(int i=1;i<=n;i++)if(a[i]>=0&&(a[i]&(1<<pos)))add(src,i,INF),add(i,src,0);
for(int i=1;i<=n;i++)if(a[i]>=0&&(!(a[i]&(1<<pos))))add(i,des,INF),add(des,i,0);
}
inline bool bfs(){
while(!q.empty())q.pop();
for(int i=0;i<=n;i++)lev[i]=0;
lev[des]=0;lev[src]=1;q.push(src);
while(!q.empty()){
int u=q.front();q.pop();
for(int j=g[u];j;j=nt[j]){
if(!c[j]||lev[v[j]])continue;
lev[v[j]]=lev[u]+1;q.push(v[j]);
if(v[j]==des)return true;
}
}
return false;
}
inline int dinic(const int &x,int f){
if(x==des)return f;
int rs=0;
for(int &j=cur[x];j;j=nt[j]){
if(!c[j]||(lev[v[j]]!=lev[x]+1))continue;
int o=dinic(v[j],min(f-rs,c[j]));
rs+=o;c[j]-=o;c[j^1]+=o;
if(rs==f)return rs;
}
return (lev[x]=0,rs);
}
inline void dfs(int now){
vis[now]=vt;a2[now]|=(1<<pos);
for(int j=g[now];j;j=nt[j]){
if(!c[j])continue;
if(vis[v[j]]!=vt)dfs(v[j]);
}
}
inline int maxflow(){
int rs=0;
while(bfs()){
int t;memcpy(cur,g,sizeof(cur));
while((t=dinic(src,INF)))rs+=t,memcpy(cur,g,sizeof(cur));
}
++vt;dfs(src);return rs;
}
}g,g2;
int main(){
n=rd(),m=rd();
for(int i=1;i<=n;i++)a[i]=rd();
for(int i=1;i<=m;i++){
int x=rd(),y=rd();
g.add(x,y,1);g.add(y,x,1);
}
for(pos=30;pos>=0;pos--){
g2=g;g2.init();ans+=(1ll<<pos)*g2.maxflow();
}
cout<<ans<<endl;ans=0;
for(int i=1;i<=n;i++)ans+=a2[i];
cout<<ans<<endl;
}