【网络流】codeforces 434D

貌似该自己搭一个blog了,但是懒得去打理...

水平考没过,感觉就像被自己骗了一样,气都没处撒...

西班牙终于还是被淘汰了,想当年第一次熬夜看球就是上届世界杯西班牙对荷兰,不禁有些英雄迟暮之感...

本来这些槽应该分开写的,但是本着吐槽一定要结合题目的精神,这道题一直没过,然后就期末了T T

题意:给出若干变量的取值范围,且知道每个变量取值的代价是个二次函数,同时变量之间有x[u[i]]<=x[v[i]]+e[i]的依赖关系,求最大权值

感觉这题的还是比较好的,用到了几个技巧

首先是直接确定最终状态的思想,我第一次见到这个思想是在2010年的有道难题http://blog.csdn.net/huyuncong/article/details/7599757

但是当时实在找不到数据,然后就忘了,这会再见一时又走到当年流量平衡的老路去了,据说srm590有类似的模型,但是我觉得有道难题的这个模型更相似,且出的时间更早一点

用b[i][j]表示变量i取j权值,b[i][j]连向b[i][j+1]为i取j的费用,b[i][r[i]]向t连取r[i]的费用,这样一个割就是一个方案,但是不是最大的,考虑另一个常用的技巧,由于每一个割都会割n条边,那么把每条边的权值赋为oo-cost的话,答案就可以化作oo*n-mincut,于是就成功转化为了最小割(如果不是统一割n条,就会发现这个技巧无法统一不同方案的代价)

然后考虑如何建立依赖条件,如果选择了b[i][j]那么b[i][j]->b[i][j+1]的边就被割开了,假设是xi<=xk,那么我们要做的就是使得b[i][j]->b[i][j+1]被割开,k割在b[k][j]之前,不能使得图割成两个集合,再观察图的特点,"b[i][j]->b[i][j+1]被割开"使得b[i][j]与s在一个集合,"k割在b[k][j]之前"使得b[k][j]和t在一个集合,那么就从b[i][j]->b[k][j]连一条无穷的边,使得仍然存在一条s->t的路径

同时要注意处理一下边界,因为定义域可能不是一一对应的

#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <iostream>
#include <algorithm>
const long long oo=(1LL)<<50;
using namespace std;
long long flow[2000000],d[2000000];
int next[2000000],sora[2000000],tail[2000000],st[2000000],po[2000000];
int n,s,t,ss,m;
int u[200000],v[200000],l[200000],r[200000],a[200000],b[200000],c[200000],e[200000],id[500][500];
void origin()
{
	s=n*300+1,t=s+1,ss=t;
	for (int i=1;i<=t;i++) tail[i]=i;
}
void link(int x,int y,long long z)
{
	++ss,next[tail[x]]=ss,tail[x]=ss,sora[ss]=y,flow[ss]=z;
	++ss,next[tail[y]]=ss,tail[y]=ss,sora[ss]=x,flow[ss]=0;
	po[ss]=ss-1,po[ss-1]=ss;
}
int bfs(int s,int t)
{
	for (int i=1;i<=t;i++) d[i]=oo;
	int h,r=h=0;
	st[r=1]=s,d[s]=0;
	for (;h<r;) {
		int ne=st[++h];
		for (int i=ne,na;next[i];) {
			i=next[i],na=sora[i];
			if (flow[i] && d[ne]+1<d[na]) {
				d[na]=d[ne]+1;
				st[++r]=na;
			}
		}
	}
	return d[t]<oo;
}
long long dfs(int x,long long low)
{
	if (x==t) return low;
	long long sum=0,tmp;
	for (int i=x,ne;next[i];) {
		i=next[i],ne=sora[i];
		if (flow[i] && d[x]+1==d[ne]) {
			if (flow[i]<low) tmp=dfs(ne,flow[i]);
			else tmp=dfs(ne,low);
			if (!tmp) d[ne]=oo;
			flow[i]-=tmp,flow[po[i]]+=tmp,sum+=tmp,low-=tmp;
			if (!low) break;
		}
	}
//    if (oo+oo==sum) cout<<x<<endl;
	return sum;
}
long long F(int i,int j)
{
	j-=101;
	return a[i]*j*j+b[i]*j+c[i];
}
int main()
{
    scanf("%d%d",&n,&m);
    origin();
    for (int i=1;i<=n;i++)
        scanf("%d%d%d",&a[i],&b[i],&c[i]);
    for (int i=1;i<=n;i++)
    		scanf("%d%d",&l[i],&r[i]),l[i]+=101,r[i]+=101;
    for (int i=1;i<=m;i++)
        scanf("%d%d%d",&u[i],&v[i],&e[i]);
    int tot=0;
    for (int i=1;i<=n;i++) {
    	link(s,tot+1,oo+oo);
    	for (int j=l[i];j<=r[i]-1;j++) {
    		id[i][j]=++tot;
    		link(tot,tot+1,oo-F(i,j));
    	}
    	id[i][r[i]]=++tot;
    	link(tot,t,oo-F(i,r[i]));
        id[i][r[i]+1]=t;
    }
    for (int i=1;i<=m;i++) {
        int L=u[i],R=v[i];
        for (int j=l[L];j<=r[L];j++) {
            int x=id[L][j],y;
            if (j-e[i]<l[R]) continue;
            y=id[R][min(j-e[i],r[R]+1)];
//            cout<<j<<' '<<min(j+e[i],r[R]+1)<<endl;
            link(x,y,oo+oo);
//            if (j==l[L] && y==t) cout<<L<<' '<<R<<endl;
        }
    }
    long long ans=0;
    for (;bfs(s,t);ans+=dfs(s,oo+oo+oo)) ;
//    cout<<oo*n<<' '<<ans<<endl;
    cout<<oo*n-ans<<endl;
    return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值