Codeforces 1146G Zoning Restrictions

题目

https://codeforces.com/contest/1146/problem/G

题目翻译

你正在准备在一条路上建房子。这条路上有n个可以建房子的点,它们从左到右编号为1~n。你可以在每个点上建一个高度为[0,h]中的整数的房子。
如果一个房子的高度为a,那么你可以获得a2的收益。
但是这个城市有m个分区限制。对于第i个分区限制,如果第li 个到第ri个点上最高的房子的高度严格大于xi,那么你就要缴纳ci的罚款。
请求出你的最大收益(房子收益总和-罚款)

题解

可以发现这道题直接做起来会比较棘手,考虑转化。
把答案转化为 所有建筑高度都为h的贡献 - num 。令a[i]表示每栋房子的实际高度,那么num=一些罚款+ ∑ h 2 − a i 2 \sum h^2-a_i^2 h2ai2
但是这样子做起来也不方便,发现数据范围特别小,因此考虑网络流。
如果要让答案尽可能大,那么num就要尽可能小。而最小割等于最大流,只需要建图就可以了。
发现难以处理高度,因此把每个点的高度拆分成独立的节点。对于每一个点,都新建一条从S到T的边数为h+2的路径,即S→0→1→2→…→h→T(这里的0~h指的是每个点拆分成不同高度),其中i→i+1(或T)的路径长度为 h 2 − i 2 h^2-i^2 h2i2。这样割掉i→i+1的边就表示选择了i这个高度。
然后罚款处理起来就很容易了,对于每个限制i,新建一个节点p,p向T连一条值为c的边,然后 [ l i , r i ] [l_i,r_i] [li,ri]中每个点的高度x+1对应的点(因为是严格大于x)都向p连一条流量为∞的边。
最后用 n ⋅ h 2 n\cdot h^2 nh2-最大流 就好了。

CODE

#include<cstdio>
using namespace std;
#define id(x,y) (x-1)*(h+1)+y+1
#define inf 999999999
#define M 20005
#define N 3055
#define S 3051
#define T 3052
int fir[N],to[M],nex[M],len[M],cur[N],gap[N],dis[N],s=1;
inline void inc(int x,int y,int z)
{
    
    
	to[++s]=y,len[s]=z,nex[s]=fir[x],cur[x]=fir[x]=s;
	to[++s]=x,len[s]=0,nex[s]=fir[y],cur[y]=fir[y]=s;
}
int dfs(int u,int flow)
{
    
    
	if(u==T) return flow;
	int v,have=flow,tmp,i;
	for(i=cur[u];i;i=nex[i])
	{
    
    
		cur[u]=i,v=to[i];
		if(len[i]&&dis[u]==dis[v]+1)
		{
    
    
			tmp=dfs(v,have<len[i]?have:len[i]);
			have-=tmp,len[i]-=tmp,len[i^1]+=tmp;
			if(!have) return flow;
		}
	}
	cur[u]=fir[u];
	if(!--gap[dis[u]]) dis[S]=T;
	++gap[++dis[u]];
	return flow-have;
}
int main()
{
    
    
	int n,h,m,l,r,x,c,i,j,ans,cnt;
	scanf("%d%d%d",&n,&h,&m);
	ans=n*h*h,cnt=id(n,h);
	for(i=1;i<=n;++i)
	{
    
    
		inc(S,id(i,0),inf);
		for(j=0;j<h;++j) inc(id(i,j),id(i,j+1),h*h-j*j);
	}
	for(i=1;i<=m;++i)
	{
    
    
		scanf("%d%d%d%d",&l,&r,&x,&c);
		if(x==h) continue;
		inc(++cnt,T,c);
		for(j=l;j<=r;++j) inc(id(j,x+1),cnt,inf);
	}
	while(dis[S]<T) ans-=dfs(S,inf);
	printf("%d\n",ans);
	return 0;
}

猜你喜欢

转载自blog.csdn.net/huangzihaoal/article/details/108738867
今日推荐