• loj #6032. 「雅礼集训 2017 Day2」水箱 线段树优化DP转移


    $ color{#0066ff}{ 题目描述 }$

    给出一个长度为 (n) 宽度为 (1) ,高度无限的水箱,有 (n-1) 个挡板将其分为 (n)(1 - 1) 的小格,然后向每个小格中注水,水如果超过挡板就会溢出到挡板的另一边,这里的水是满足物理定律的(在无挡板阻拦的情况下会向低处流),现在有 (m) 个条件 ((i,j,k)),表示从左到右数的第 (i) 个格子中,在高度为 (y+0.5) 的地方是否有水, (k=1) 表示有水,(k=0) 表示没有水,请求出这 (m) 个条件最多能同时满足多少个条件。本题有多组数据。

    (color{#0066ff}{输入格式})

    第一行一个正整数 (T) 为数据组数。
    第二行两个正整数 (n, m),中间用空格隔开。
    接下来一行 (n-1) 个整数,表示从左到右每一块隔板的高度。
    接下来 (m) 行,每行三个整数 (i,j,k),表示一个条件。

    (color{#0066ff}{输出格式})

    (T) 行,每行对应一组数据的答案。

    (color{#0066ff}{输入样例})

    2
    3 4
    3 4
    1 3 1
    2 1 0
    2 2 0
    3 3 1
    2 2
    2
    1 2 0
    1 2 1
    

    (color{#0066ff}{输出样例})

    3
    1
    

    (color{#0066ff}{数据范围与提示})

    对于(20\%)的数据,(n,mle 16)

    对于另外(10\%)的数据,只存在指明某处有水的条件

    对于另外(30\%)的数据,(n,mle 1000)

    对于(100\%)的数据,(n,mle 10^5, Tle 5)

    (color{#0066ff}{题解})

    显然我们可以有一个(O(n^2))的暴力

    (f[i][j])表示从(1)(i),且第i格水的高度为j的最大答案

    转移的话,如果当前这个没超,就是它根下一个是分开的,那么就有转移(f[i][j] o f[i+1][k], j,kin[0,h[i]])

    如果超过了,那么就是这两个连在一起了,那么就这样转移(f[i][j] o f[i+1][j])

    然后发现,第一维可以省去,然后在结合转移,发现其实就是两个操作

    一个是用([0,h[i]])的最大值更新([0,h[i]])的每一个值,(另一个转移是直接继承,不用管)

    还有就是对于每个条件,比如一个条件(x, 0),就是说,x的位置没有水,那么显然(f[0...x])这些值都要+1,这是区间加

    我们只需要维护一个资瓷区间加,区间取max,区间求max的线段树就行了

    注意h要离散化一下qwq

    #include<bits/stdc++.h>
    #define LL long long
    LL in() {
    	char ch; LL x = 0, f = 1;
    	while(!isdigit(ch = getchar()))(ch == '-') && (f = -f);
    	for(x = ch ^ 48; isdigit(ch = getchar()); x = (x << 1) + (x << 3) + (ch ^ 48));
    	return x * f;
    }
    const int maxn = 5e5 + 10;
    const int inf = 0x7fffffff;
    struct Tree {
    	protected:
    		struct node {
    			node *ch[2];
    			int l, r, add, max, val;
    			node(int l = 0, int r = 0, int add = 0, int max = 0, int val = 0): l(l), r(r), add(add), max(max), val(val) { ch[0] = ch[1] = NULL; }
    			void trn(int x, int y) { add += x, max = std::max(max + x, y), val = std::max(val + x, y); }
    			void dwn() {
    				if(!add && !max) return;
    				ch[0]->trn(add, max), ch[1]->trn(add, max);
    				add = max = 0;
    			}
    			void upd() { val = std::max(ch[0]->val, ch[1]->val); }
    			int mid() { return (l + r) >> 1; }
    		}*root;
    		void build(node *&o, int l, int r) {
    			o = new node(l, r);
    			if(l == r) return;
    			build(o->ch[0], l, o->mid());
    			build(o->ch[1], o->mid() + 1, r);
    		}
    		void lazy(node *o, int l, int r, int x, int y) {
    			if(l <= o->l && o->r <= r) return o->trn(x, y);
    			o->dwn();
    			if(l <= o->mid()) lazy(o->ch[0], l, r, x, y);
    			if(r > o->mid()) lazy(o->ch[1], l, r, x, y);
    			o->upd();
    		}
    	 	int query(node *o, int l, int r) {
    	 		if(l <= o->l && o->r <= r) return o->val;
    			o->dwn(); int ans = 0;
    			if(l <= o->mid()) ans = std::max(ans, query(o->ch[0], l, r));
    			if(r > o->mid()) ans = std::max(ans, query(o->ch[1], l, r));
    			o->upd();
    			return ans;
    		}	
    	public:
    		void add(int l, int r, int x, int y) { if(l <= r) lazy(root, l, r, x, y); }
    		void init(int n) { root = NULL, build(root, 1, n); }
    		int query(int l, int r) { return l <= r? query(root, l, r) : 0; }
    		int getans() { return root->val; }
    }T;
    int n, m, h[maxn], b[maxn], cnt;
    std::vector<int> mp0[maxn], mp1[maxn];
    int main() {
    	for(int S = in(); S --> 0;) {
    		n = in(), m = in();
    		h[1] = b[cnt = 1] = inf;
    		int x, y, z;
    		for(int i = 1; i <= n; i++) mp0[i].clear(), mp1[i].clear();
    		for(int i = 2; i <= n; i++) b[++cnt] = h[i] = in();
    		h[n + 1] = 0;
    		for(int i = 1; i <= m; i++) {
    			x = in(), y = in(), z = in();
    			if(z) mp1[x].push_back(y);
    			else mp0[x].push_back(y);
    			b[++cnt] = y, b[++cnt] = y + 1;
    		}
    		std::sort(b + 1, b + cnt + 1);
    		cnt = std::unique(b + 1, b + cnt + 1) - b - 1;
    		T.init(cnt);
    		for(int i = 1; i <= n; i++) h[i] = std::lower_bound(b + 1, b + cnt + 1, h[i]) - b;
    		for(int i = 1; i <= n; i++) {
    			for(int now : mp0[i]) T.add(1, std::lower_bound(b + 1, b + cnt + 1, now) - b, 1, 0);
    			for(int now : mp1[i]) T.add(std::lower_bound(b + 1, b + cnt + 1, now) - b + 1, cnt, 1, 0);
    			T.add(1, h[i + 1], 0, T.query(1, h[i + 1]));
    		}
    		printf("%d
    ", T.getans());
    	}
    	return 0;
    }
    
  • 相关阅读:
    C语言学习趣事_BT_C_Code_混乱编程代码分析_1
    随想系列_6_终于被我发现Microsoft的一个错误了
    C语言学习趣事_经典面试题系列_2
    C++_系列自学课程_第_2_课_牛刀小试
    PLC_自动化控制系统_1_简说自动化控制系统
    随想系列_5_乱七八糟
    Android Handler使用
    Android Layout 布局属性
    MotionEvent事件在onInterceptTouchEvent()、onTouchEvent()中的传递顺序【转】
    AndroidMenifest 有关SdkVersion 说明
  • 原文地址:https://www.cnblogs.com/olinr/p/10613440.html
Copyright © 2020-2023  润新知