题目链接: https://codeforces.com/contest/1146/problem/G
题意:
你现在要给 n n n 个位置制定高度 h i ( 1 < = h i < = H ) h_i (1<=h_i<=H) hi(1<=hi<=H) ,每个位置 i i i 的高度可以带来 h i 2 h_i^2 hi2 的收益,但是有 m m m 条限制,每个限制 j j j 的信息为 l j , r j , x j , c j l_j,r_j,x_j,c_j lj,rj,xj,cj 表示从位置 l j l_j lj 位置 r j r_j rj 当中如果有任意一个位置的高度超过了 x j x_j xj 那么就要罚款 c j c_j cj ,现在问你最大的总收益为多少。
做法:
因为 n , H , m n,H,m n,H,m 的大小均不超过 50 50 50 ,所以还是很明显能感受到是最大流的。套路无非就是 原最大收益-最小代价,代价在 ① ① ① 只到达这个高度的损失 和 ② ② ② 到达这个高度所要接受的罚款 里面选出更小的,那么这个更小的值的就靠 最大流=最小割 来维护了。
我想了挺久的,其实一开始方向蛮对的,每个位置每个高度一个点,每个条件一个点,但是不知道为什么后来就偏了,想用区间来表示(脑子瓦特了还顺着这个错误的方向想了很久)。
略过略过,其实沿着一开始的思路的话还是很顺的想的。把点按照高度拆开,高度 z z z 向高度 z + 1 z+1 z+1 连一条只达到高度 z + 1 z+1 z+1 时要损失的代价。然后将限制条件 m j m_j mj中每一个合法的位置 p o s ( l j < = p o s < = r j ) pos(l_j<=pos<=r_j) pos(lj<=pos<=rj) 的高度为 x j + 1 x_j+1 xj+1 的点向限制条件连一条边, m j m_j mj 本身向汇点连一条代价这么多的边即可。
画了个丑陋的图,第一个样例帮助理解。
代码
#include<bits/stdc++.h>
#define rep(i,a,b) for(int i = (int)a;i<=(int)b;i++)
#define pb push_back
#define lson rt<<1
#define rson rt<<1|1
#define mid (l+r)/2
using namespace std;
const int maxn=2800;
const int maxm=30005;
const int inf = 1<<30;
typedef long long ll;
int nex[maxm],to[maxm],cap[maxm],from[maxm];
int n,m,h,cnt,head[maxn],d[maxn],sp,tp;//原点,汇点
//理论复杂度n2*m
void add(int u,int v,int c){
//printf("from = %d to = %d cap = %d\n",u,v,c);
from[cnt]=u,to[cnt]=v,cap[cnt]=c,nex[cnt]=head[u],head[u]=cnt++;
from[cnt]=v,to[cnt]=u,cap[cnt]=0,nex[cnt]=head[v],head[v]=cnt++;
}
int bfs(){
queue <int> q;
memset(d,-1,sizeof(d));
d[sp]=0;
q.push(sp);
while(!q.empty()){
int cur=q.front();
q.pop();
for(int i=head[cur];i!=-1;i=nex[i]){
int u=to[i];
if(d[u]==-1 && cap[i]>0){
d[u]=d[cur]+1;
q.push(u);
}
}
}
return d[tp] != -1;
}
int dfs(int a,int b){
int r=0;
if(a==tp)return b;
for(int i=head[a];i!=-1 && r<b;i=nex[i])
{
int u=to[i];
if(cap[i]>0 && d[u]==d[a]+1)
{
int x=min(cap[i],b-r);
x=dfs(u,x);
r+=x;
cap[i]-=x;
cap[i^1]+=x;
}
}
if(!r)d[a]=-2;
return r;
}
int dinic(int sp,int tp){
int total=0,t;
while(bfs()){
while(t=dfs(sp,inf))
total+=t;
}
return total;
}
int gain(int id,int hei){
return (id-1)*(h+1)+hei+1;
}
int sub(int x){
return h*h-x*x;
}
int main(){
cnt=0;
memset(head,-1,sizeof(head));
scanf("%d%d%d",&n,&h,&m);
int ans=n*h*h,np=(h+1)*n;
sp=0,tp=np+m+1;
rep(i,1,n){
add(sp,gain(i,0),inf);
rep(j,1,h){
add(gain(i,j-1),gain(i,j),sub(j-1));
}
}
rep(i,1,m){
int l,r,x,c; scanf("%d%d%d%d",&l,&r,&x,&c);
if(x==h) continue;
rep(j,l,r){
add(gain(j,x+1),np+i,inf);
}
add(np+i,tp,c);
}
printf("%d\n",ans-dinic(sp,tp));
return 0;
}