正题
题目链接:https://www.luogu.com.cn/problem/P8215
题目大意
有2×n2\times n2×n个人,第2×i−12\times i-12×i−1和第2×i2\times i2×i个人一组,然后每个人可以选择愿不愿意合作,愿意需要付出cic_ici代价,不愿意是did_idi代价,如果两个人都愿意,可以选择合作。如果一个人选择愿意另一个人不愿意,那么愿意的那个人会产生eie_iei的代价。
然后给出mmm对关系,然后题目描述开摆了
1≤n≤5000,0≤m≤10000,1≤ai,bi,ci,di,ei≤1091\leq n\leq 5000,0\leq m\leq 10000,1\leq a_i,b_i,c_i,d_i,e_i\leq 10^91≤n≤5000,0≤m≤10000,1≤ai,bi,ci,di,ei≤109
解题思路
首先考虑c,d,ec,d,ec,d,e的贡献,考虑用最小割。
首先c,dc,dc,d要选一个,所以它们肯定是串在一个点上的,所以S→i→TS\rightarrow i\rightarrow TS→i→T,SSS到iii的流量为ccc,iii到TTT的流量为ddd。
然后eee的贡献就是一个选ccc一个选ddd时的贡献,所以我们可以让iii连向它的队友,流量为eee,如果iii选了ccc,它队友选了ddd,那么这条eee的边也要割。
然后因为还有一个选择是否合作,但是要求所有的都选择ccc的贡献,所以我们让新建一个点WWW,S→WS\rightarrow WS→W的一条边表示是否合作(如果连了就表示合作),然后WWW连向同一个队的两个点,
那么一个小组的图大概就长这样
然后考虑后面的贡献就很麻烦了
这一个我们可以直接让BBB所在组的WWW点连接AAA点就行了,如果AAA选了ddd且BBB的合作边没被割掉,那么刚好这条路就是通的,也需要被割掉。
至于
这个贡献,我们考虑反过来处理。
我们发现S→WiS\rightarrow W_iS→Wi这条边还没有权值,我们可以先默认如果AAA没有合作就会产生aia_iai的代价,然后如果BBB选择了不愿意,那么就会少产生aia_iai的贡献,我们让BBB的ddd值减去aia_iai。
然后发现如果此时AAA没有合但是BBB不愿意也会少产生这个贡献,我们让WAW_AWA连向BBB费用为aia_iai,这样这种情况就不会少算了。
然后因为ddd可能减到负数,我们可以让所有的ccc和ddd加上一个大整数,然后最后统一减去这部分贡献就好了。
这样就完美处理完了所有的条件
code
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
#define ll long long
using namespace std;
const ll N=5100*6;
struct node{ll to,next,w;
}a[N<<4];
ll n,m,s,t,c[N],d[N],e[N],w[N];
ll tot=1,ls[N],dep[N],ans;
queue<ll> q;
void addl(ll x,ll y,ll w){a[++tot].to=y;a[tot].next=ls[x];ls[x]=tot;a[tot].w=w;a[++tot].to=x;a[tot].next=ls[y];ls[y]=tot;a[tot].w=0;return;
}
bool bfs(){while(!q.empty())q.pop();memset(dep,0,sizeof(dep));dep[s]=1;q.push(s);while(!q.empty()){ll x=q.front();q.pop();for(ll i=ls[x];i;i=a[i].next){ll y=a[i].to;if(!a[i].w||dep[y])continue;dep[y]=dep[x]+1;if(y==t)return 1;q.push(y);}}return 0;
}
ll dinic(ll x,ll flow){ll rest=0,k;if(x==t)return flow;for(ll i=ls[x];i;i=a[i].next){ll y=a[i].to;if(!a[i].w||dep[y]!=dep[x]+1)continue;rest+=(k=dinic(y,min(flow-rest,a[i].w)));a[i].w-=k;a[i^1].w+=k;if(rest==flow)return flow;}if(!rest)dep[x]=0;return rest;
}
signed main()
{scanf("%lld%lld",&n,&m);s=3*n+1;t=s+1;for(ll i=1;i<=2*n;i++)scanf("%lld%lld%lld",&c[i],&d[i],&e[i]);for(ll i=1,x,y,a,b;i<=m;i++){scanf("%lld%lld%lld%lld",&x,&y,&a,&b);ll A=(x+1)/2,B=(y+1)/2;addl(2*n+B,x,b);w[A]+=a;d[y]-=a;addl(2*n+A,y,a);}for(ll i=1;i<=2*n;i++){addl(s,i,d[i]+1e14);addl(i,t,c[i]+1e14);ll p=(i&1)?(i+1):(i-1);addl(i,p,e[i]);}for(ll i=1;i<=n;i++){addl(s,2*n+i,w[i]);addl(2*n+i,2*i-1,1e18);addl(2*n+i,2*i,1e18);}while(bfs())ans+=dinic(s,1e18);printf("%lld\n",ans-200000000000000ll*n);return 0;
}