• Codeforces 1108E (Array and Segments) 线段树


    题意:给你一个长度为n的序列和m组区间操作,每组区间操作可以把区间[l, r]中的数字都-1,请选择一些操作(可以都不选),使得序列的最大值和最小值的差值尽量的大。

    思路:容易发现如果最大值和最小值都在某个操作区间里,那么这个操作没有意义,因为差值没变,所以我们可以想到暴力枚举每一个位置,假设这个位置的数是最小的,那么就把所有与他相关的区间操作都执行,执行完后找到当前序列的最大值更新答案即可。

    E1数据很小,直接3重循环暴力枚举就可以过了,复杂度为O(n * n * m)。

    对于E2,很明显如果每次操作完了从头到尾循环一遍找最大值很费时间,看标题也能想到最大值要用线段树来维护(2333),但是就算用线段树找最大值,复杂度还是O(n * m * logn)。

    我们可以直观感受一下,对于每个枚举的位置,每次都暴力的把可以的区间操作加上,再暴力的还原这些操作,非常的浪费,所有我们可以从这里优化。

    假设一个操作区间为[l, r],那么实际这个区间操作可以使[l, r]区间之中的值变得更小,在这个范围之外,这个操作是多余的。所以我们可以用差分的思想,我们记录一下这个区间对答案影响的开始位置和结束位置,扫描到区间开始时在线段树中加上该区间的影响,到区间末尾时消去该区间的影响。

    因为每个操作只会添加和消去一次, 总复杂度为O(n * logn + m * logn), 其中的n * logn 是线段树的建树时间。

    代码:

    #include <bits/stdc++.h>
    #define ls(x) (x << 1)
    #define rs(x) ((x << 1) | 1)
    #define pii pair<int, int>
    #define INF 0x3f3f3f3f
    using namespace std;
    const int maxn = 100010;
    int a[maxn];
    
    struct node{
    	int mx, add;
    	int l, r;
    };
    
    vector<int> st[maxn], ed[maxn];
    
    node tr[maxn * 4];
    pii b[310];
    vector<int> res;
    void pushdown(int o) {
    	if(tr[o].add != 0) {
    		tr[ls(o)].add += tr[o].add;
    		tr[rs(o)].add += tr[o].add;
    		tr[ls(o)].mx += tr[o].add;
    		tr[rs(o)].mx += tr[o].add;
    		tr[o].add = 0;
    	}
    }
    
    void maintain(int o) {
    	tr[o].mx = max(tr[ls(o)].mx,tr[rs(o)].mx);
    }
    void build(int o, int l, int r) {
    	if(l == r) {
    		tr[o].mx = a[l];
    		tr[o].add = 0;
    		tr[o].l = l;
    		tr[o].r = r;
    		return;
    	}
    	tr[o].l = l;
    	tr[o].r = r;
    	int mid = (l + r) >> 1;
    	build(ls(o), l, mid);
    	build(rs(o), mid + 1, r);
    	maintain(o);
    }
    
    void update(int o, int l, int r, int ql, int qr, int add) {
    	if(l >= ql && r <= qr) {
    		tr[o].mx += add;
    		tr[o].add += add;
    		return;
    	}
    	pushdown(o);
    	int mid = (l + r) >> 1;
    	if(mid >= ql) update(ls(o), l, mid, ql, qr, add);
    	if(mid < qr) update(rs(o), mid + 1, r, ql, qr, add);
    	maintain(o);
    }
    
    int query(int o, int l, int r, int ql ,int qr) {
    	if(l >= ql && r <= qr) {
    		return tr[o].mx;
    	}
    	int ans = -INF;
    	pushdown(o);
    	int mid = (l + r) >> 1;
    	if(ql <= mid) ans = max(ans, query(ls(o), l, mid, ql, qr));
    	if(qr > mid) ans = max(ans, query(rs(o), mid + 1, r, ql, qr));
    	return ans;
    }
    
    int main() {
    	int n, m, ans = 0;
    	int mx = - INF, mi = INF;
    //	freopen("in.txt","r",stdin);
    	scanf("%d%d", &n, &m);
    	for(int i = 1; i <= n; i++) {
    		scanf("%d", &a[i]);
    		mx = max(mx, a[i]);
    		mi = min(mi, a[i]);
    	}
    	ans = mx - mi;
    	build(1, 1, n);
    	for(int i = 1; i <= m; i++) {
    		scanf("%d%d",&b[i].first, &b[i].second);
    		st[b[i].first].push_back(b[i].second);
    		ed[b[i].second].push_back(b[i].first);
    	}
    	int pos = 0, tmp;
    	for(int i = 1; i <= n; i++) {
    		for(int j = 0; j < st[i].size(); j++) {
    			update(1, 1, n, i, st[i][j], -1);
    		}
    		tmp = query(1, 1, n, 1, n) - query(1, 1, n, i, i);
    		if(tmp > ans) {
    			ans = tmp;
    			pos = i;
    		}
    		for(int j = 0; j < ed[i].size(); j++) {
    			update(1, 1, n, ed[i][j], i, 1);
    		}
    	}
    	printf("%d
    ", ans);
    	for(int i = 1; i <= m; i++) {
    		if(b[i].first <= pos && b[i].second >= pos)
    			res.push_back(i);
    	}
    	printf("%d
    ",res.size());
    	for(int i = 0; i < res.size(); i++)
    		printf("%d ",res[i]);
    } 
    

      

  • 相关阅读:
    2021-04-02:给定一个正方形或者长方形矩阵matrix,实现zigzag打印。[[0,1,2],[3,4,5],[6,7,8]]的打印顺序是0,1,3,6,4,2,5,7,8。
    2021-04-01:给定一个正方形矩阵matrix,原地调整成顺时针90度转动的样子。[[a,b,c],[d,e,f],[g,h,i]]变成[[g,d,a],[h,e,b],[i,f,c]]。
    2021-03-31:给定一个数组arr,给定一个值v。求子数组平均值小于等于v的最长子数组长度。
    2021-03-30:给定一个整数组成的无序数组arr,值可能正、可能负、可能0。给定一个整数值K,找到arr的所有子数组里,哪个子数组的累加和<=K,并且是长度最大的。返回其长度。
    2021-03-29:无序数组arr,子数组-1和1的数量一样多,请问最长子数组的长度是多少?
    04Null和Undefined
    03数据类型
    win10 命令行下 重启虚拟网卡
    JavaScript注释及命名规范
    第一个javascrpt代码
  • 原文地址:https://www.cnblogs.com/pkgunboat/p/10320830.html
Copyright © 2020-2023  润新知