【bzoj3511】土地划分
跟bzoj3894文理分科类似的题,只是多了如果x和y在不同集合就要扣ec的条件。
直接x和y连一条ec的双向边就好了……都是套路了……但是我脑子做的时候脑子有点坑……搞了这样的做法:
ans+=ea+eb+ec,ea+=ec,eb+=ec;
正确性:
同在A王国里:ans-eb-ec=ea;
同在B王国里:ans-ea-ec=eb;
不同王国:ans-ea-ec-eb-ec=-ec;
想出如此机(zhi)智(zhang)的方法我还沾沾自喜了一下【捂脸】
(也算提供了另外对于两点不同集合限制的另一种解法了是吧,咋们还省了一条边呢【大雾】)
#include<cstdio>
int n,m,t,head[100005],h[100005],x,d[100005],num=1,id;
long long ans;
int min(int a,int b){return a<b?a:b;}
struct node{int to,next,flow;}e[800005];
bool bfs()
{
for(int i=1;i<=t;i++)d[i]=0;d[0]=1;
int l=0,r=1;
while(l<r)
{
int u=h[++l];
for(int i=head[u];i;i=e[i].next)
if(e[i].flow)
{
int v=e[i].to;
if(!d[v])d[v]=d[u]+1,h[++r]=v;
}
}
return d[t];
}
int dfs(int u,int flow)
{
if(u==t)return flow;
int ans=0;bool b=0;
for(int i=head[u];i;i=e[i].next)
if(e[i].flow)
{
int v=e[i].to;if(d[v]!=d[u]+1)continue;
int tmp=dfs(v,min(e[i].flow,flow));
if(tmp)
{
b=1;e[i].flow-=tmp,e[i^1].flow+=tmp;
flow-=tmp,ans+=tmp;if(!flow)return ans;
}
}
if(!b)d[u]=-1e9;
return ans;
}
void add(int u,int v,int flow)
{
e[++num]=(node){v,head[u],flow},head[u]=num;
e[++num]=(node){u,head[v],0},head[v]=num;
}
int main()
{
scanf("%d%d",&n,&m);t=n+m*2+1;
add(0,1,2e9),add(n,t,2e9);id=n;
for(int i=2;i<n;i++)scanf("%d",&x),add(0,i,x),ans+=x;
for(int i=2;i<n;i++)scanf("%d",&x),add(i,t,x),ans+=x;
for(int i=1;i<=m;i++)
{
int x,y,a,b,c;
scanf("%d%d%d%d%d",&x,&y,&a,&b,&c);
ans+=a+b+c,a+=c,b+=c;
id++,add(x,id,2e9),add(y,id,2e9),add(id,t,b);
id++,add(0,id,a),add(id,x,2e9),add(id,y,2e9);
}
while(bfs())ans-=dfs(0,2e9);
printf("%lld\n",ans);
}
另一种解法:
对于(x,y,ea,eb,ec)的限制,从s向x,y分别连边ea/2,从x,y向t分别连边eb/2,
x和y连双向边ea/2+eb/2+ec
正确性:
ans=ea+eb;
ans-eb/2-eb/2=ea
ans-ea/2-ea/2=eb
ans-ea/2-eb/2-ea/2-eb/2-ec=-ec
可以看出这个方法是靠(x,y)这条边权的“自定义”来保证正确性的,限制也只能针对两个点,因此好像也不能推广到多个点的情况orz仅适用于两点的限制这样的问题,在两点限制问题的处理上非常的优秀,据说是一个经典模型。