提交答案题(网络流)
计算一次函数的值实在是太难了,所以为了简化问题,我们计算二次函数。现在给了你\(N\)个二次函数,第\(i\)个二次函数的形状为\(f_i(x_i) = a_i {x_i}^2 + b_i x_i + c_i , l_i \leq x_i \leq r_i\)。同时我们又给了你\(M\)个限制关系,限制关系的形式为\(x_u \leq x_v + d\)。现在告诉你所有这些信息,你需要最大化\(\sum_{i = 1}^{N} f_i(x_i)\)的值。
\(1 \leq N \leq 50 , 0 \leq M \leq 100 , |a_i| \leq 10 , |b_i| , |c_i| \leq 1000 , ?100 \leq l_i \leq r_i \leq 100 , 1 \leq u , v \leq N , u \neq v , |d| \leq 200\)
听说这种坐标限制相关的题目都可以用网络流,与二次函数无关?
对于每个二次函数的区间,把所有区间上的整点都建在网络流的点上,依次连接(同时头要向src,为要向dst连接),一个点\(x_i\in[l_{now}, r_{now}]\)的值是\(f_{now}(x_i)\)的值。那么一个区间就构成了一条链。如果不考虑限制,网络流这样跑出来就是正确的。
由于有坐标限制,例如\(x_u<x_v+d\)。考虑这个式子的含义,意思是说,如果选定了\(x_v\)的一个值a,那么必须满足\(x_v>a-d\)。因此用网络流在两个链上连若干INF边即可。
这个做法的点数是\(n*200=1e4\),边数是\(1e4+M*200=3e4\)。用dinic是能过的~
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int maxf=55, maxn=maxf*200, maxm=maxn+100, INF=1e9, inf=1e8;
int n, m, cnt, src, dst, ans;
inline int min(int x, int y){ return x<y?x:y; }
struct Edge{
int to, nxt, f;
}e[maxm*2];
int fir[maxn], cnte=1;
void addedge(int x, int y, int v){
Edge &ed=e[++cnte];
//printf("%d %d\n", x, y);
ed.to=y; ed.nxt=fir[x]; ed.f=v; fir[x]=cnte;
}
int q[maxn], head, tail, dep[maxn];
bool bfs(){
memset(dep, 0, sizeof(dep)); dep[src]=1;
head=tail=0; q[tail++]=src; int u;
while (head<tail){
u=q[head++];
for (int i=fir[u]; i; i=e[i].nxt)
if (e[i].f&&!dep[e[i].to]){
dep[e[i].to]=dep[u]+1;
q[tail++]=e[i].to;
}
}
return dep[dst];
}
int cur[maxn];
int dfs(int u, int flow){
if (u==dst) return flow;
for (int i=cur[u]; i; i=e[i].nxt, cur[u]=i)
if (dep[e[i].to]==dep[u]+1&&e[i].f){
int minm=dfs(e[i].to, min(flow, e[i].f));
e[i].f-=minm; e[i^1].f+=minm;
if (minm) return minm;
}
return 0;
}
int Dinic(){
int ans=0, t;
while (bfs()){
memcpy(cur, fir, sizeof(fir));
while (t=dfs(src, INF)) ans+=t;
}
return ans;
}
int a[maxf], b[maxf], c[maxf], l[maxf], r[maxf];
int beg[maxn]; //代表第i个函数的起始点编号
int main(){
int u, v, d;
scanf("%d%d", &n, &m); src=++cnt; dst=++cnt; int val;
for (int i=0; i<n; ++i) scanf("%d%d%d", &a[i], &b[i], &c[i]);
for (int i=0; i<n; ++i){
scanf("%d%d", &l[i], &r[i]);
addedge(src, ++cnt, 2*inf);
addedge(cnt, src, 0);
beg[i]=cnt;
for (int j=l[i]; j<=r[i]; ++j){
val=a[i]*j*j+b[i]*j+c[i];
addedge(cnt, cnt+1, inf-val);
addedge(cnt+1, cnt, 0);
++cnt;
}
addedge(cnt, dst, 2*inf);
addedge(dst, cnt, 0);
} int x, y;
while (m--){
scanf("%d%d%d", &u, &v, &d); --u; --v;
for (int i=l[v]; i<=r[v]; ++i){
int j=i+d;
if (j>r[u]) continue;
//beg[u]+j-l[u]表示x[u]=x[v]+d的点的编号
x=beg[u]+max(0, j-l[u]+1); y=beg[v]+i-l[v]+1;
addedge(x, y, 2*inf); addedge(y, x, 0);
}
}
printf("%d\n", inf*n-Dinic());
return 0;
}
原文地址:https://www.cnblogs.com/MyNameIsPc/p/9380558.html