提交答案题(网络流)

提交答案题(网络流)

计算一次函数的值实在是太难了,所以为了简化问题,我们计算二次函数。现在给了你\(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;
}
posted @ 2018-07-28 08:24  pechpo  阅读(603)  评论(0编辑  收藏  举报