• 【XSY2718】gift 分数规划 网络流


    题目描述

      有(n)个物品,买第(i)个物品要花费(a_i)元。还有(m)对关系:同时买(p_i,q_i)两个物品会获得(b_i)点收益。

      设收益为(B),花费为(A),求(lceilfrac{B}{A} ceil-1)

      (nleq 400,mleq 200000,1leq a_i,b_ileq 100)

    题解

      显然这是一个分数规划问题。

      二分答案(s),判断答案是否能大于(s)

    [egin{align} frac{B}{A}&>s\ B&>As\ B-As&>0 end{align} ]

      问题转化成买一个物品获得(-a_is)点收益,求收益是否能(>0)

      显然这个可以用最大权闭合子图来做,点数为(n+m+2),边数为(n+3m),肯定会TLE

      考虑另一种连边方法。

      对于每个点(i),连边((S,i,-a_is))

      对于每一对关系,列一个方程,解得:连边((p_i,q_i,frac{1}{2}b_i),(q_i,p_i,frac{1}{2}b_i),(p_i,T,frac{1}{2}b_i),(q_i,T,frac{1}{2}b_i))

      答案就是(sum_{i=1}^mb_i-maxflow)

      因为容量为分数不好处理,所以可以把全部数( imes 2)

      这样做的点数是(n+2),边数是(2n+m)

      写ISAP不用卡常美滋滋。

    代码

    #include<cstdio>
    #include<cstring>
    #include<queue>
    using namespace std;
    typedef long long ll;
    int rd()
    {
    	int s=0,c;
    	while((c=getchar())<'0'||c>'9');
    	s=c-'0';
    	while((c=getchar())>='0'&&c<='9')
    		s=s*10+c-'0';
    	return s;
    }
    int sum=0;
    namespace flow
    {
    	int c[10000010];
    	int v[10000010];
    	int t[10000010];
    	int h[4010];
    	int n;
    	void add(int x,int y,int a)
    	{
    		n++;
    		v[n]=y;
    		c[n]=a;
    		t[n]=h[x];
    		h[x]=n;
    	}
    	void init()
    	{
    		memset(h,0,sizeof h);
    		n=0;
    	}
    	int S,T;
    	int num;
    	int d[4010];
    	int e[4010];
    	int cur[4010];
    	int op(int x)
    	{
    		return ((x-1)^1)+1;
    	}
    	queue<int> q;
    	void bfs()
    	{
    		memset(d,-1,sizeof d);
    		memset(e,0,sizeof e);
    		d[T]=0;
    		q.push(T);
    		int x,i;
    		while(!q.empty())
    		{
    			x=q.front();
    			q.pop();
    			e[d[x]]++;
    			for(i=h[x];i;i=t[i])
    				if(c[op(i)]&&d[v[i]]==-1)
    				{
    					d[v[i]]=d[x]+1;
    					q.push(v[i]);
    				}
    		}
    	}
    	int dfs(int x,int flow)
    	{
    		if(x==T)
    			return flow;
    		int s=0,u;
    		for(int &i=cur[x];i;i=t[i])
    			if(d[v[i]]==d[x]-1&&c[i])
    			{
    				u=dfs(v[i],min(flow,c[i]));
    				s+=u;
    				flow-=u;
    				c[i]-=u;
    				c[op(i)]+=u;
    				if(!flow)
    					return s;
    			}
    		e[d[x]]--;
    		if(!e[d[x]])
    			d[S]=num;
    		e[++d[x]]++;
    		cur[x]=h[x];
    		return s;
    	}
    	int solve()
    	{
    		bfs();
    		memcpy(cur,h,sizeof h);
    		int ans=0;
    		while(ans<2*sum&&d[S]>=0&&d[S]<=num-1)
    			ans+=dfs(S,2*sum-ans);
    		return ans;
    	}
    }
    using flow::S;
    using flow::T;
    using flow::num;
    int n,m;
    int lx[2000010];
    int ly[2000010];
    int lz[2000010];
    int s[4010];
    int a[4010];
    void add(int x,int y,int z)
    {
    	flow::add(x,y,z);
    	flow::add(y,x,0);
    }
    void add2(int x,int y,int z)
    {
    	flow::add(x,y,z);
    	flow::add(y,x,z);
    }
    int check(int x)
    {
    	flow::init();
    	int i;
    	S=n+1;
    	num=T=n+2;
    	for(i=1;i<=n;i++)
    	{
    		add(S,i,int(min(0x7fffffffll,2ll*a[i]*x)));
    		add(i,T,s[i]);
    	}
    	for(i=1;i<=m;i++)
    		if(lx[i]!=ly[i])
    			add2(lx[i],ly[i],lz[i]);
    	return flow::solve()<2*sum;
    }
    int main()
    {
    #ifndef ONLINE_JUDGE
    	freopen("c.in","r",stdin);
    	freopen("c.out","w",stdout);
    #endif
    //	scanf("%d%d",&n,&m);
    	n=rd();
    	m=rd();
    	int i;
    	for(i=1;i<=n;i++)
    //		scanf("%d",&a[i]);
    		a[i]=rd();
    	for(i=1;i<=m;i++)
    	{
    //		scanf("%d%d%d",&lx[i],&ly[i],&lz[i]);
    		lx[i]=rd();
    		ly[i]=rd();
    		lz[i]=rd();
    		s[lx[i]]+=lz[i];
    		s[ly[i]]+=lz[i];
    		sum+=lz[i];
    	}
    	int l=0,r=sum;
    	while(l<r)
    	{
    		int mid=(l+r+1)>>1;
    		if(check(mid))
    			l=mid;
    		else
    			r=mid-1;
    	}
    	printf("%d
    ",l);
    	return 0;
    }
    
  • 相关阅读:
    课堂作业1(出题)
    微信小程序开发1
    asp.net实现通用水晶报表
    JS实现网络拓扑图
    注册与登录 接口与模板
    cookies and session
    Django路由及get请求post请求
    简析JavaScript事件冒泡机制
    屏蔽运营商流量球分析及解决方法
    使用github搭建个人主页
  • 原文地址:https://www.cnblogs.com/ywwyww/p/8513516.html
Copyright © 2020-2023  润新知