[2018.4.23 T3] 最大值

本文介绍了一种利用网络流算法解决发电站最大发电量问题的方法。通过建立最小割模型,确保每个发电站能在限制条件下达到最大发电量。文章详细解释了如何根据发电站的级别和限制条件构建网络流图,并给出了具体的实现代码。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目描述

Shy 有 n 个发电站。每个发电站有一个 level(可正可负的整数),i 号发电站的 level要在 l[i],r[i]之间(包含),Level x 会带来 fi(x)的发电量。

Shy 还有 m 个限制。限制是这样的形式,x[u]≤x[v]+d,表示 u 的 level 小于等于 v 的level 加 d(d 是整数)。

请问最大发电量是多少。

输入格式

第一行两个整数 n,m 表示发电站的数目和限制的数目;

接下来 n 行,每行三个整数ai,bi,ciai,bi,ci 表示 fififi(x)=aixx+bix+cifi(x)=ai∗x∗x+bi∗x+ci

接下来 n 行每行两个整数 l[i],r[i];

接下来 m 行,每行三个整数 u,v,d,表示 x[u]x[v]+dx[u]≤x[v]+d

输出格式

一个正整数表示答案。

输入样例

5 8
1 -8 20
2 -4 0
-1 10 -10
0 1 0
0 -1 1
1 9
1 4
0 10
3 11
7 9
2 1 3
1 2 3
2 3 3
3 2 3
3 4 3
4 3 3
4 5 3
5 4 3

输出样例

46

提示
数据规模

对于 30%的数据,1≤n≤3;

对于 100%的数据,
1n50,0m100,|ai|10,|bi|1000,|ci|1000,100|li||ri|1001uvnuv|di|2001≤n≤50,0≤m≤100,|ai|≤10,|bi|≤1000,|ci|≤1000,−100≤|li|≤|ri|≤100,1≤u,v≤n,u≠v,|di|≤200

解题分析:

发现此题数据范围只有50,容易想到这是一道网络流的题目。 显然一组解是使得每个发电站发电量最大的和, 并且每个发电站都能发电, 那么我们可以用最小割模型来处理每个发电站都能发电的情况。

f(x)f(x)表示某个发电站在取到x时能发电的量。倘若没有限制条件,我们设某个发电站的可行区间为[i,j][i,j],那么我们就可以将在可行区间内的取值视为ji+1j−i+1点, 将每个点之间的流量定为f(x)f(x), 连成一条链, 将一头一尾分别与源点和汇点连接。这样建出的图跑最小割就会保证每条链上都会取到一个值。

然而我们要的是最大割。实际上我们取f(x)−f(x)再加上一个很大的值保证其为正数即可。

现在考虑加上限制。x[u]dx[v]x[u]−d≤x[v], 因为我们是最小割模型, 所以要保证uu所在链的取值减去一个常数要小于v所在链的取值, 直接将差值为d的两条链上的点连一条流量为infinf的边即可。 这样就保证了在最小割的情况下, 要割掉uu所在链上的m点, 就要保证v所在链上的大于等于m+dm+d位置的点一定也要被割(否则还是联通的)。 剩下的就只有打板啦…

PS:此题一定要加当前弧优化, 否则会T 20-30 分…

代码如下:

#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <cctype>
#include <cmath>
#include <algorithm>
#include <queue>
#define R register
#define gc getchar()
#define IN inline
#define W while
#define trans 19260817
#define inf 99999999
#define MX 1000005
#define st 0
#define ed 500000
static bool fu;
template <class T>
IN void in (T &x)
{
    fu = false; x = 0; R char c = gc;
    W (!isdigit(c))
    {if(c == '-') fu = true; c = gc;}
    W (isdigit(c))
    {x = (x << 1) + (x << 3) + c - 48, c = gc;}
    if(fu) x = -x;
}
struct Dat
{
    int a, b, c;
}dat[55];
int from[55], head[MX], lef[55], rig[55], cnt = -1, dot, num, cpy[MX];
struct Edge
{
    int to, flow, nex;
}edge[MX];
IN int cal(const int &typ, const int &pos)
{   return -(dat[typ].a * pos * pos + dat[typ].b * pos + dat[typ].c) + trans;}
IN void addedge(const int &from, const int &to, const int &len)
{
    edge[++cnt] = (Edge){to, len, head[from]};
    head[from] = cnt;
}
namespace Dinic
{
    int layer[MX];
    std::queue <int> q;
    IN bool BFS()
    {
        R int now;
        memset(layer, 0, sizeof(layer));
        layer[st] = 1;
        q.push(st);
        W (!q.empty())
        {
            now = q.front();
            q.pop();
            for (R int i = head[now]; ~i; i = edge[i].nex)
            {
                if(layer[edge[i].to] || !edge[i].flow) continue;
                layer[edge[i].to] = layer[now] + 1;
                q.push(edge[i].to);
            }
        }
        return layer[ed];
    }
    int DFS(int now, int value)
    {
        if(now == ed || !value) return value;
        int ret = 0, tmp;
        for (R int &i = head[now]; ~i; i = edge[i].nex)
        {
            if(!edge[i].flow || layer[edge[i].to] != layer[now] + 1) continue;
            tmp = DFS(edge[i].to, std::min(value - ret, edge[i].flow));
            if(!tmp) continue;
            edge[i].flow -= tmp;
            edge[i ^ 1].flow += tmp;
            ret += tmp;
            if(value == ret) return value;
        }
        return ret;
    }
    long long Dinic_init()
    {
        long long ans = 0;
        std::memcpy(cpy, head, 500001);
        W (BFS()) 
        {   
            ans += DFS(st, inf);
            std::memcpy(head, cpy, 500001);
        }       ans = ans - dot * trans;
        ans = -ans;
        return ans;
    }
}
using namespace Dinic;
int main(void)
{
    memset(head, -1, sizeof(head));
    int jcq, ncq, a, b, c;
    in(dot), in(num);
    for (R int i = 1; i <= dot; ++i) in(dat[i].a), in(dat[i].b), in(dat[i].c);
    for (R int i = 1; i <= dot; ++i) in(lef[i]), in(rig[i]);
    for (R int i = 1; i <= dot; ++i)
    {
        jcq = i * 605;//两个点间预留600空间, 存它的取值范围内的点, 避免重复。
        addedge(st, jcq + lef[i], inf), addedge(jcq + rig[i] + 1, ed, inf);
        addedge(jcq + lef[i], st, 0), addedge(ed, jcq + rig[i] + 1, 0);
        for (R int j = lef[i]; j <= rig[i]; ++j) 
        addedge(jcq + j, jcq + j + 1, cal(i, j)), addedge(jcq + j + 1, jcq + j, 0);
    }
    for (R int i = 1; i <= num; ++i)
    {
        in(a), in(b), in(c);
        jcq = a * 605;
        ncq = b * 605;
        int sta = std::max(lef[a], lef[b] + c);
        int endd = std::min(rig[b] + c, rig[a]) + 1;//一定注意要加1, 因为两个端点也要算在里面
        for (R int j = sta; j <= endd; ++j) 
        addedge(jcq + j, ncq + j - c, inf), addedge(ncq + j - c, jcq + j, 0);
        addedge(jcq + rig[a] + 1, ncq + rig[a] + 1 - c, inf);
        addedge(ncq + rig[a] + 1 - c, jcq + rig[a] + 1, 0);
    }
    printf("%lld", Dinic_init());

}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值