zoukankan      html  css  js  c++  java
  • 【BZOJ2400】Spoj 839 Optimal Marks 最小割

    【BZOJ2400】Spoj 839 Optimal Marks

    Description

    定义无向图中的一条边的值为:这条边连接的两个点的值的异或值。
    定义一个无向图的值为:这个无向图所有边的值的和。
    给你一个有n个结点m条边的无向图。其中的一些点的值是给定的,而其余的点的值由你决定(但要求均为非负数),使得这个无向图的值最小。在无向图的值最小的前提下,使得无向图中所有点的值的和最小。

    Input

    第一行,两个数n,m,表示图的点数和边数。
    接下来n行,每行一个数,按编号给出每个点的值(若为负数则表示这个点的值由你决定,值的绝对值大小不超过10^9)。
    接下来m行,每行二个数a,b,表示编号为a与b的两点间连一条边。(保证无重边与自环。)

    Output

        第一行,一个数,表示无向图的值。
        第二行,一个数,表示无向图中所有点的值的和。

    Sample Input

    3 2
    2
    -1
    0
    1 2
    2 3

    Sample Output

    2
    2

    HINT

    数据约定
      n<=500,m<=2000
    样例解释
        2结点的值定为0即可。

    题解:先拆位,然后每个数要么是0要么是1,这显然就转换成了一个最小割问题。

    只考虑所有-1的点i,从S->i连一条边代表i有多少1与i相连,从i->T连一条边代表有多少0与i相连,从i->j连一条边代表i,j不同所付出的代价。

    但是要求点权和最小怎么办?将边权*10000+点权即可。

    #include <cstdio>
    #include <cstring>
    #include <iostream>
    #include <queue>
    using namespace std;
    typedef long long ll;
    int n,m,ans,S,T,cnt;
    ll ans1,ans2;
    int pa[2010],pb[2010],v[510],d[510],head[510],next[10010],val[10010],to[10010],s1[510],s2[510];
    queue<int> q;
    int rd()
    {
    	int ret=0,f=1;	char gc=getchar();
    	while(gc<'0'||gc>'9')	{if(gc=='-')	f=-f;	gc=getchar();}
    	while(gc>='0'&&gc<='9')	ret=ret*10+gc-'0',gc=getchar();
    	return ret*f;
    }
    void add(int a,int b,int c)
    {
    	to[cnt]=b,val[cnt]=c,next[cnt]=head[a],head[a]=cnt++;
    	to[cnt]=a,val[cnt]=0,next[cnt]=head[b],head[b]=cnt++;
    }
    int dfs(int x,int mf)
    {
    	if(x==T)	return mf;
    	int i,k,temp=mf;
    	for(i=head[x];i!=-1;i=next[i])
    	{
    		if(d[to[i]]==d[x]+1&&val[i])
    		{
    			k=dfs(to[i],min(temp,val[i]));
    			if(!k)	d[to[i]]=0;
    			val[i]-=k,val[i^1]+=k,temp-=k;
    			if(!temp)	break;
    		}
    	}
    	return mf-temp;
    }
    int bfs()
    {
    	memset(d,0,sizeof(d));
    	while(!q.empty())	q.pop();
    	int i,u;
    	q.push(S),d[S]=1;
    	while(!q.empty())
    	{
    		u=q.front(),q.pop();
    		for(i=head[u];i!=-1;i=next[i])
    		{
    			if(!d[to[i]]&&val[i])
    			{
    				d[to[i]]=d[u]+1;
    				if(to[i]==T)	return 1;
    				q.push(to[i]);
    			}
    		}
    	}
    	return 0;
    }
    void work(int x)
    {
    	int i;
    	S=0,T=n+1;
    	memset(head,-1,sizeof(head)),cnt=ans=0;
    	memset(s1,0,sizeof(s1)),memset(s2,0,sizeof(s2));
    	for(i=1;i<=n;i++)
    	{
    		if(v[i]>=0)	ans2+=(v[i]&x);
    		else	s2[i]=1;
    	}
    	for(i=1;i<=m;i++)
    	{
    		if(v[pa[i]]>=0)
    		{
    			ans1+=((v[pa[i]]&x)^(v[pb[i]]&x));
    		}
    		else	if(v[pb[i]]>=0)
    		{
    			if(v[pb[i]]&x)	s1[pa[i]]+=10000;
    			else	s2[pa[i]]+=10000;
    		}
    		else	add(pa[i],pb[i],10000),add(pb[i],pa[i],10000);
    	}
    	for(i=1;i<=n;i++)	if(v[i]<0)	add(S,i,s1[i]),add(i,T,s2[i]);
    	while(bfs())	ans+=dfs(S,1<<30);
    	ans1+=(ll)x*(ans/10000),ans2+=(ll)x*(ans%10000);
    }
    int main()
    {
    	n=rd(),m=rd();
    	int i;
    	for(i=1;i<=n;i++)	v[i]=rd();
    	for(i=1;i<=m;i++)
    	{
    		pa[i]=rd(),pb[i]=rd();
    		if(v[pa[i]]>v[pb[i]])	swap(pa[i],pb[i]);
    	}
    	for(i=0;i<=30;i++)	work(1<<i);
    	printf("%lld
    %lld",ans1,ans2);
    	return 0;
    }
    //3 2 3 -1 2 1 2 2 3
  • 相关阅读:
    java基础(初始化和清理)
    jquery的常用操作(转载)+ 开发中经常犯的错误总结(原创) (不断补充)
    java基础常见错误归纳(值传递和引用传递)
    FormPanel 综合使用 忆江南
    MyEclipse下Jquery代码自动提示 忆江南
    HQL查询 忆江南
    MD5密码保护 忆江南
    FormPanel数据提交 忆江南
    新手上路
    编码总结,以及对BOM的理解
  • 原文地址:https://www.cnblogs.com/CQzhangyu/p/7297737.html
Copyright © 2011-2022 走看看