【二元关系网络流】JZOJ5506. 【清华冬令营2018模拟】变量

Description

  • 有n个变量w[1]~w[n],每个变量可以取W或-W。
  • 有p个式子,形如Hi=ai|w[xi]-w[yi]|+bi|w[yi]-w[zi]|+ci|w[zi]-w[xi]|+di(w[xi]-w[yi])+ei(w[yi]-w[zi])+fi(w[zi]-w[xi])。
  • 有q个条件,形如w[x]<=w[y]或w[x]=w[y]或w[x]<w[y]。
  • 最小化sigma(wi)+sigma(Hi)。
  • 对于100%的数据,t<=10,n<=500,p,q<=1000,W<=1e6,系数<=1e3, 所有数字均为非负整数。

Solution

  • 简单做一下总结,有关这一类二元组的选择以及关系的建图。
  • 首先我们需要建的是最小割的模型而不是费用流(
  • 每个变量一个点,以及源汇两点共 n + 2 n+2 n+2个点。
  • 对于每一个变量有 0 , 1 0,1 0,1两种状态,对应图上与 S , T S,T S,T相连的两条边,割掉 ( S , x ) (S,x) (S,x)即为选1,反之割掉 ( x , T ) (x,T) (x,T)即为选择0。
  • 流量为选择 0 / 1 0/1 0/1的贡献。
  • a x = 0 , a y = 1 a_x=0,a_y=1 ax=0,ay=1 v v v贡献: ( y , x , v ) (y,x,v) (y,x,v)
  • a x = a y = 0 a_x=a_y=0 ax=ay=0 v v v贡献: ( S , n e w , v ) , ( n e w , x , i n f ) , ( n e w , y , i n f ) (S,new,v),(new,x,inf),(new,y,inf) (S,new,v),(new,x,inf),(new,y,inf)
  • a x = a y = 1 a_x=a_y=1 ax=ay=1 v v v贡献: ( x , n e w , i n f ) , ( y , n e w , i n f ) , ( n e w , T , v ) (x,new,inf),(y,new,inf),(new,T,v) (x,new,inf),(y,new,inf),(new,T,v)
  • a x a_x ax a y a_y ay之间的限制可以考虑将上面的 v v v变成 i n f inf inf对应加边。
  • 跑最小割即可得到最小贡献。
  • 最大贡献可以考虑求上面所有限制的补集,然后从总的里面减去一个最小割。
我的辣鸡当前弧
	for(int &i=cur[x];i&&res;i=nx[i]) if (ec[i]>0&&dis[x]+1==dis[e[i]]){
		ll tmp=dg(e[i],min(res,ec[i]));
		ec[i]-=tmp,ec[i^1]+=tmp,res-=tmp;
	}
正确写法
	for(int &i=cur[x];i&&res;i=nx[i]) if (ec[i]>0&&dis[x]+1==dis[e[i]]){
		ll tmp=dg(e[i],min(res,ec[i]));
		ec[i]-=tmp,ec[i^1]+=tmp,res-=tmp;
		if (!res) break;
	}
  • 因为可能res为0的时候退出for循环还会执行跳当前弧的操作,但实际上这个当前弧还没有流满,所以要在for循环之前退出。
#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
#define maxn 505
#define maxm 20005
#define inf 1e13
#define ll long long 
using namespace std;

int Tt,n,w,p,q,i,j,k,S,T,tot,tp[maxn];
int em,e[maxm],nx[maxm],ls[maxn];ll ec[maxm],s[maxn],ans;

void insert(int x,int y,ll z){
	em++,e[em]=y,nx[em]=ls[x],ls[x]=em,ec[em]=z;
	em++,e[em]=x,nx[em]=ls[y],ls[y]=em,ec[em]=0;
}

void read(int &x){
	x=0; char ch=getchar();
	for(;ch<'0'||ch>'9';ch=getchar());
	for(;ch>='0'&&ch<='9';ch=getchar()) x=x*10+ch-'0';
}

void add(int x,int y,int v,int d){
	insert(x,y,v*2),insert(y,x,v*2);
	s[x]+=d,s[y]-=d;
}

int d[maxn],dis[maxn],cur[maxn];
int bfs(){
	memset(dis,0,sizeof(dis));
	int t=0,w=1;d[1]=S,dis[S]=1;
	while (t<w){
		int x=d[++t]; cur[x]=ls[x];
		for(int i=ls[x];i;i=nx[i]) if (ec[i]&&!dis[e[i]])
			dis[e[i]]=dis[x]+1,d[++w]=e[i];
	}
	return dis[T]>0;
}

ll dg(int x,ll p){
	if (x==T) return p;
	ll res=p;
	for(int &i=cur[x];i&&res;i=nx[i]) if (ec[i]>0&&dis[x]+1==dis[e[i]]){
		ll tmp=dg(e[i],min(res,ec[i]));
		ec[i]-=tmp,ec[i^1]+=tmp,res-=tmp;
		if (!res) break;
	}
	return p-res;
}

ll maxflow(){
	ll sum=0;
	while (bfs())
		sum+=dg(S,inf);
	return sum+ans;
}

int main(){
//	freopen("variable.in","r",stdin);
//	freopen("variable.out","w",stdout);
	read(Tt);
	while (Tt--){
		read(n),read(w),read(p),read(q);
		memset(tp,0,sizeof(tp));
		memset(s,0,sizeof(s));
		memset(ls,0,sizeof(ls)),em=1;
		S=n+1,T=n+2,ans=0;
		for(i=1;i<=p;i++){
			int x,y,z,a,b,c,d,e,f;
			read(x),read(y),read(z),read(a),read(b),read(c),read(d),read(e),read(f);
			add(x,y,a,d),add(y,z,b,e),add(z,x,c,f);
		}
		for(i=1;i<=q;i++){
			int x,y,r;
			read(x),read(y),read(r);
			if (r==0) insert(y,x,inf);
			if (r==1) insert(x,y,inf),insert(y,x,inf);
			if (r==2) tp[y]=1,tp[x]=-1;
		}
		for(i=1;i<=n;i++){
			s[i]++,ans-=abs(s[i]);
			insert(S,i,(tp[i]==-1)?inf:(abs(s[i])+s[i]));
			insert(i,T,(tp[i]==1)?inf:(abs(s[i])-s[i]));
		}
		printf("%lld\n",maxflow()*w);
	}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值