JZOJ 7045. 2021.04.05【2021省赛模拟】数学考试(网络流)
JZOJ 7045. 2021.04.05【2021省赛模拟】数学考试
题目大意
- 给出 n n n个三次函数,其中 x i x_i xi为第 i i i个函数的自变量,取值为 [ l i , r i ] [l_i,r_i] [li,ri],另还需满足形如 x a ≤ x b + d x_a\le x_b+d xa≤xb+d的 m m m个限制,求 ∑ f i ( x i ) \sum f_i(x_i) ∑fi(xi)的最大值。
- n , ∣ l i ∣ , ∣ r i ∣ ≤ 100 , m ≤ 500 n,|l_i|,|r_i|\le 100,m\le 500 n,∣li∣,∣ri∣≤100,m≤500
题解
- 限制特别难处理,一般会考虑建图跑网络流。本题采用的是求最小割。
- 每个函数从 l i l_i li到 r i r_i ri依次从源点到汇点连一条链,表示 p ( p ∈ [ l i , r i ] ) p(p\in[l_i,r_i]) p(p∈[li,ri])的点连出的边权为 − f i ( p ) -f_i(p) −fi(p),断一条边表示该函数所取的自变量,则此处限制了自变量的取值。
- 对每个限制,移项变为 x b ≥ x a − d x_b\ge x_a-d xb≥xa−d,即每取一个 x a x_a xa,则需保证 x b ≥ x a − d x_b\ge x_a-d xb≥xa−d,那么从 a a a的每个点 x a x_a xa向 b b b中表示 x a − d x_a-d xa−d的点连边,边权为 i n f inf inf。其含义为若 a a a的自变量为 x a x_a xa,则必须 b b b函数 x a − d x_a-d xa−d以后取自变量(断一条边)。
- 为了保证边权为正,给所有边都加上一个大常数 C C C,最后答案为最小割减去 n ∗ C n*C n∗C再取相反数。
代码
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define N 110
#define M 510
#define R 210
#define ll long long
#define E 100000000
int last[N * R], nxt[R * (N + M) * 2], to[R * (N + M) * 2], len;
int cur[N * R], gap[N * R], dis[N * R];
int p[N][R], n, m, S, T, tot;
ll ls[R * (N + M) * 2];
struct {
int a, b, c, d, l, r;
}a[N];
int ct(int k, int i) {
return i * i * i * a[k].a + i * i * a[k].b + i * a[k].c + a[k].d;
}
void add(int x, int y, ll w) {
to[++len] = y;
nxt[len] = last[x];
ls[len] = w + E;
last[x] = len;
to[++len] = x;
nxt[len] = last[y];
ls[len] = 0;
last[y] = len;
}
ll dfs(int k, ll flow)
{
if(k == T) return flow;
ll have = 0;
for(int i = cur[k]; i; i = nxt[i])
{
cur[k] = i;
if(dis[to[i]] + 1 == dis[k] && ls[i])
{
ll now = dfs(to[i], min(flow - have, ls[i]));
have += now;
ls[i] -= now, ls[i ^ 1] += now;
if(flow == have) return have;
}
}
cur[k] = last[k];
gap[dis[k]]--;
if(!gap[dis[k]]) dis[S] = tot;
gap[++dis[k]]++;
return have;
}
int main() {
int tn;
scanf("%d", &tn);
while(tn--) {
int i, j, x, y, d;
memset(last, 0, sizeof(last));
len = 1;
scanf("%d%d", &n, &m);
tot = 2, S = 1, T = 2;
for(i = 1; i <= n; i++) {
scanf("%d%d%d%d%d%d", &a[i].a, &a[i].b, &a[i].c, &a[i].d, &a[i].l, &a[i].r);
add(S, tot + 1, 1e12);
for(j = a[i].l; j < a[i].r; j++) {
p[i][j + 100] = ++tot;
add(tot, tot + 1, -ct(i, j));
}
p[i][a[i].r + 100] = ++tot;
add(tot, T, -ct(i, a[i].r));
}
for(i = 1; i <= m; i++) {
scanf("%d%d%d", &x, &y, &d);
for(j = a[x].l; j <= a[x].r; j++) {
if(j - d <= a[y].l) continue;
if(j - d > a[y].r) add(p[x][j + 100], T, 1e12);
else add(p[x][j + 100], p[y][j + 100 - d], 1e12);
}
}
memset(gap, 0, sizeof(gap));
memset(cur, 0, sizeof(cur));
memset(dis, 0, sizeof(dis));
gap[0] = tot;
ll sum = 0;
while(dis[S] < tot) sum += dfs(S, 1e15);
sum -= (ll)n * E;
if(sum >= 1e10) puts("mei ji ge");
else printf("%lld\n", -sum);
}
return 0;
}
自我小结
- 本以为是一道数学题,但后来才发现函数只是个幌子。
- 这题”奇奇怪怪的限制就想到网络流“理应很自然,但意识还是略有欠缺。
- 知道网络流后连边建图解决限制求答案都十分自然。
哈哈哈哈哈哈哈哈哈哈