• 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_ale 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,mle 500 n,li,ri100m500

    题解

    • 限制特别难处理,一般会考虑建图跑网络流。本题采用的是求最小割。
    • 每个函数从 l i l_i li r i r_i ri依次从源点到汇点连一条链,表示 p ( p ∈ [ l i , r i ] ) p(pin[l_i,r_i]) p(p[li,ri])的点连出的边权为 − f i ( p ) -f_i(p) fi(p),断一条边表示该函数所取的自变量,则此处限制了自变量的取值。
    • 对每个限制,移项变为 x b ≥ x a − d x_bge x_a-d xbxad,即每取一个 x a x_a xa,则需保证 x b ≥ x a − d x_bge 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
    ", -sum);
    	}
    	return 0;
    }
    

    自我小结

    • 本以为是一道数学题,但后来才发现函数只是个幌子。
    • 这题”奇奇怪怪的限制就想到网络流“理应很自然,但意识还是略有欠缺。
    • 知道网络流后连边建图解决限制求答案都十分自然。
    哈哈哈哈哈哈哈哈哈哈
  • 相关阅读:
    【转】修改mysql数据库的用户名和密码
    oracle 11g密码过期问题解决方法
    配置网络YUM源
    RedHat 7.0更新升级openSSH7.4p1
    Linux下端口被占用解决
    js function 中的返回值
    代码笔记1
    element模态框dialog中的select组件中选中无反应无显示
    vue如何使用rules对表单字段进行校验
    关于JSON.parse(JSON.stringify(obj))实现深拷贝应该注意的坑
  • 原文地址:https://www.cnblogs.com/LZA119/p/14623426.html
Copyright © 2020-2023  润新知