1 条题解

  • 0
    @ 2025-8-24 22:36:59

    自动搬运

    查看原文

    来自洛谷,原作者为

    avatar Exber
    星铁:152282910 | 博客:https://rebxe.github.io/

    搬运于2025-08-24 22:36:59,当前版本为作者最后更新于2022-03-16 16:57:53,作者可能在搬运后再次修改,您可在原文处查看最新版

    自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多

    以下是正文


    做法

    最小割。

    这是我第一次在赛场上做出有难度的网络流,写篇题解纪念一下。

    赛后发现我的建模方法和官方题解并不相同,所以这篇题解也算是提供了一种新奇的建图思路吧。

    首先观察到每个人有两种选择:愿意和不愿意。那么可以用源点表示愿意,汇点表示不愿意。具体就是对第 ii 个人建立节点 ii,然后从源点向 ii 连一条流量为 did_i 的边,从 ii 向汇点连一条流量为 cic_i 的边

    然后考虑同一组内的两个人 x,yx,y 意见不同(xx 选择了愿意)的情况,此时情况如下:

    因为会产生 exe_x 的不满,所以xxyy 连一条流量为 exe_x 的边来增加不满

    对于 yy 选择了愿意但是 xx 选择了不愿意的情况亦然。

    加上这些边之后的图如下:(两个奇奇怪怪的点是用来防止边权重叠的)

    接下来我们就需要解决最棘手的喜欢关系了。(赛场上想了 1h 左右/ll)

    首先可以发现,只有一组里两个人都选择愿意才可以合作。所以可以给每一组引入一个点 xix_i,从 xix_i 分别向两个成员连流量为 inf\inf 的边

    这么连边的目的是,假设有一些流量送到了 xix_i

    那么就可以保证每一组如果不合作的话给 xix_i 送流量的边都要被割断,如果合作的话就不隔断,并且如果一组中有一个或以上人选择了不愿意那么就必须不合作。换句话说,xix_i 没有流量代表这一组不合作,否则代表这一组合作

    现在我们可以表示合不合作了,接下来考虑喜欢关系的连边。

    若第 jj 组关系是 xjx_j 喜欢 yjy_juu 表示 xjx_j 那一组的 xix_ivv 表示 yjy_j 那一组的 xix_i,那么:

    如果 xjx_j 没有和队友合作,并且 yjy_j 选择了愿意,在图上就是 yjy_j 有流量并且 uu 不能有流量。此时会产生 aja_j 的不满,那么我们可以从有流量的 yjy_j 向不能有流量的 uu 连一条流量为 aja_j 的边

    如果 xjx_j 选择了不愿意,并且 yjy_j 和队友合作了,在图上就是 xjx_j 连向汇点的边没有被割断并且 vv 有流量。此时会产生 bjb_j 的不满,那么我们可以从有流量的 vv 向可以到达汇点的 xjx_j 连一条流量为 bjb_j 的边

    加上这些边后的图:(假设有一条喜欢关系:22 喜欢 33

    这样我们就建完图了,跑最小割即可。

    AC 代码

    #include <iostream>
    #include <algorithm>
    #include <cstdio>
    #include <cstring>
    #include <cmath>
    #include <queue>
    #include <vector>
    #include <map>
    #include <set>
    
    using namespace std;
    
    struct node
    {
    };
    
    typedef long long ll;
    
    const ll S=5000005,MS=1000005;
    
    int n,m,s,t;
    int xid[MS];
    int esum,to[S],nxt[S],h[MS];
    ll c[S];
    int dep[MS];
    
    inline void init()
    {
    	esum=1;
    	memset(h,0,sizeof(h));
    	s=0;
    	t=1000003;
    }
    
    inline void add(int x,int y,ll w)
    {
    	c[++esum]=w;
    	to[esum]=y;
    	nxt[esum]=h[x];
    	h[x]=esum;
    }
    
    inline bool bfs()
    {
    	memset(dep,0,sizeof(dep));
    	queue<int> q;
    	q.push(s);
    	dep[s]=1;
    	while(!q.empty())
    	{
    		int u=q.front();
    		q.pop();
    		for(int i=h[u];i;i=nxt[i])
    		{
    			int v=to[i];
    			if(c[i]>0&&dep[v]==0)
    			{
    				dep[v]=dep[u]+1;
    				q.push(v);
    			}
    		}
    	}
    	return dep[t]!=0;
    }
    
    ll dfs(int u,ll w)
    {
    	if(u==t)
    	{
    		return w;
    	}
    	ll sum=0;
    	for(int i=h[u];i;i=nxt[i])
    	{
    		int v=to[i];
    		if(c[i]>0&&dep[v]==dep[u]+1)
    		{
    			ll re=dfs(v,min(w,c[i]));
    			c[i]-=re;
    			c[i^1]+=re;
    			sum+=re;
    			w-=re;
    			if(w==0)
    			{
    				break;
    			}
    		}
    	}
    	if(sum==0)
    	{
    		dep[u]=0;
    	}
    	return sum;
    }
    
    inline ll dinic()
    {
    	ll ans=0;
    	while(bfs())
    	{
    		ans+=dfs(s,1e17);
    	}
    	return ans;
    }
    
    inline void slove()
    {
    	scanf("%d%d",&n,&m);
    	n*=2;
    	init();
    	for(int i=1;i<=n;i++)
    	{
    		ll C,D,E;
    		scanf("%lld%lld%lld",&C,&D,&E);
    		add(s,i,D);
    		add(i,s,0);
    		add(i,t,C);
    		add(t,i,0);
    		int v=(i&1)?i+1:i-1;
    		add(i,v,E);
    		add(v,i,0);
    	}
    	for(int i=1;i<=n;i+=2)
    	{
    		int u=n+(i+1)/2;
    		add(u,i,1e17);
    		add(i,u,0);
    		add(u,i+1,1e17);
    		add(i+1,u,0);
    		xid[i]=u;
    		xid[i+1]=u;
    	}
    	for(int i=1;i<=m;i++)
    	{
    		int x,y;
    		ll a,b;
    		scanf("%d%d%lld%lld",&x,&y,&a,&b);
    		int u1=xid[y],v1=xid[x];
    		add(u1,x,b);
    		add(x,u1,0);
    		add(y,v1,a);
    		add(v1,y,0);
    	}
    	printf("%lld\n",dinic());
    }
    
    int main()
    {
    	int _=1;
    //	scanf("%d",&_);
    	while(_--)
    	{
    		slove();
    	}
    	return 0; 
    }
    
    • 1

    信息

    ID
    7569
    时间
    1000ms
    内存
    512MiB
    难度
    6
    标签
    递交数
    0
    已通过
    0
    上传者