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 xaxb+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,ri100m500

题解

  • 限制特别难处理,一般会考虑建图跑网络流。本题采用的是求最小割。
  • 每个函数从 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 xbxad,即每取一个 x a x_a xa,则需保证 x b ≥ x a − d x_b\ge x_a-d xbxad,那么从 a a a的每个点 x a x_a xa b b b中表示 x a − d x_a-d xad的点连边,边权为 i n f inf inf。其含义为若 a a a的自变量为 x a x_a xa,则必须 b b b函数 x a − d x_a-d xad以后取自变量(断一条边)。
  • 为了保证边权为正,给所有边都加上一个大常数 C C C,最后答案为最小割减去 n ∗ C n*C nC再取相反数。

代码

#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;
}

自我小结

  • 本以为是一道数学题,但后来才发现函数只是个幌子。
  • 这题”奇奇怪怪的限制就想到网络流“理应很自然,但意识还是略有欠缺。
  • 知道网络流后连边建图解决限制求答案都十分自然。
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值