zoukankan      html  css  js  c++  java
  • @luogu


    @description@

    有一个 n×n 的矩阵 a,初始全是 0,有 m 次修改操作和 q 次查询操作,先进行所有修改操作,然后进行所有查询操作。

    一次修改操作会给出 l1,l2,r1,r2,x,代表把所有满足 l1≤i≤r1 且 l2≤j≤r2 的 a[i][j] 元素加上一个值 x。

    一次查询操作会给出 l1,l2,r1,r2,代表查询所有满足 l1≤i≤r1 且 l2≤j≤r2 的 a[i][j] 元素的最大值。

    原题链接。

    @solution@

    考虑扫描线,修改操作看成在 l1 处执行区间 (l2, r2) 加 x,r1 + 1 处执行区间 (l2, r2) 减 x。
    这样可以做到 O((m + n)*log + q*n)。

    尝试以提高修改复杂度为代价降低查询复杂度。
    枚举询问左端点 l1',从最左边(注意这里是最左边)左往右作扫描线,并从 l1' 开始维护历史最大值。
    这样只需在 r1' 处查询区间 [l2', r2'] 历史最大值即可。可以做到 O((m + n)*n*log + q*log)。

    因为 n, m 和 q 的范围并不对等,所以 O(q*log) 对于 q 而言是能接受的最大复杂度。
    考虑一种分治:每次分治到 [L, R] 时仅处理经过中点 mid 的询问,将询问拆成 [11', mid] + [mid, r1']。
    一次从 mid 出发,从左往右扫描;一次从 mid 出发,从右往左扫描,并维护历史最大值。

    不过我们扫描线必须要从最左边/最右边开始(否则就会出现奇奇怪怪的东西),但这样复杂度没法保证。
    可以开两棵可持久化线段树(使用标记永久化),用 mid 处的版本去初始化我们要维护历史最大值的线段树。
    这里的初始化使用 tag 的方法,不然复杂度也会爆炸。。。

    然后复杂度应该是 O(m*log^2 + (n + q)*log),就可以通过此题了。

    有一个小细节。某一时刻我们必须先加入该时刻的区间减操作再加入该时刻的区间加操作。
    否则维护历史最大值可能就是出现只取这一时刻的一部分操作,然而这是不合法的。

    @accepted code@

    #include <cstdio>
    #include <vector>
    using namespace std;
    
    typedef long long ll;
    
    const int MAXN = 50000;
    const int MAXQ = 500000;
    
    struct segtree1{
    	struct node{
    		ll tag, mx;
    		node *ch[2];
    	}pl[100*MAXN + 5], *ncnt, *NIL;
    	segtree1() {
    		ncnt = NIL = pl;
    		NIL->tag = NIL->mx = 0, NIL->ch[0] = NIL->ch[1] = NIL;
    	}
    	node *newnode() {
    		node *p = (++ncnt);
    		p->tag = p->mx = 0, p->ch[0] = p->ch[1] = NIL;
    		return p;
    	}
    	node *modify(node *pre, int l, int r, int ql, int qr, ll d) {
    		if( qr < l || ql > r ) return pre;
    		node *nw = newnode(); (*nw) = (*pre);
    		if( ql <= l && r <= qr )
    			nw->mx += d, nw->tag += d;
    		else {
    			int m = (l + r) >> 1;
    			nw->ch[0] = modify(pre->ch[0], l, m, ql, qr, d);
    			nw->ch[1] = modify(pre->ch[1], m + 1, r, ql, qr, d);
    			nw->mx = max(nw->ch[0]->mx, nw->ch[1]->mx) + nw->tag;
    		}
    		return nw;
    	}
    }T1, T2;
    segtree1::node *rt1[MAXN + 5], *rt2[MAXN + 5];
    
    struct segtree2{
    	#define lch (x << 1)
    	#define rch (x << 1 | 1)
    	
    	int le[4*MAXN + 5], ri[4*MAXN + 5];
    	bool dt[4*MAXN + 5]; segtree1::node *nd[4*MAXN + 5];
    	ll tg[4*MAXN + 5], htg[4*MAXN + 5], mx[4*MAXN + 5], hmx[4*MAXN + 5];
    	
    	void build(int x, int l, int r) {
    		le[x] = l, ri[x] = r;
    		if( l == r ) return ;
    		int m = (l + r) >> 1;
    		build(lch, l, m), build(rch, m + 1, r);
    	}
    	void maintain1(int x, segtree1::node *t) {
    		dt[x] = true;
    		hmx[x] = mx[x] = t->mx;
    		htg[x] = tg[x] = 0;
    		nd[x] = t;
    	}
    	void maintain2(int x, ll hd, ll d) {
    		htg[x] = max(htg[x], tg[x] + hd), tg[x] += d;
    		hmx[x] = max(hmx[x], mx[x] + hd), mx[x] += d;
    	}
    	void pushup(int x) {
    		hmx[x] = max(hmx[lch], hmx[rch]) + nd[x]->tag;
    		mx[x] = max(mx[lch], mx[rch]) + nd[x]->tag;
    	}
    	void pushdown(int x) {
    		if( dt[x] ) {
    			maintain1(lch, nd[x]->ch[0]);
    			maintain1(rch, nd[x]->ch[1]);
    			dt[x] = false;
    		}
    		if( tg[x] || htg[x] ) {
    			maintain2(lch, htg[x], tg[x]);
    			maintain2(rch, htg[x], tg[x]);
    			tg[x] = htg[x] = 0;
    		}
    	}
    	void modify(int x, int l, int r, ll d) {
    		if( l > ri[x] || r < le[x] )
    			return ;
    		if( l <= le[x] && ri[x] <= r ) {
    			maintain2(x, d, d);
    			return ;
    		}
    		pushdown(x);
    		modify(lch, l, r, d);
    		modify(rch, l, r, d);
    		pushup(x);
    	}
    	ll query(int x, int l, int r) {
    		if( l > ri[x] || r < le[x] ) return 0;
    		if( l <= le[x] && ri[x] <= r ) return hmx[x];
    		pushdown(x);
    		return max(query(lch, l, r), query(rch, l, r)) + nd[x]->tag;
    	}
    }t1, t2;
    
    struct query{
    	int l1, r1, l2, r2, id;
    }qry[MAXQ + 5];
    ll ans[MAXQ + 5];
    
    struct node{
    	int l, r, x; node() : l(0), r(0), x(0) {}
    	node(int _l, int _r, int _x) : l(_l), r(_r), x(_x) {}
    };
    vector<node>v1[MAXN + 5], v2[MAXN + 5], vl[MAXN + 5], vr[MAXN + 5];
    void solve(int l, int r, int k) {
    	if( !k ) return ;
    	int m = (l + r) >> 1;
    	for(int i=l;i<=r;i++) vl[i].clear(), vr[i].clear();
    	bool flag = false;
    	for(int i=1;i<=k;i++)
    		if( qry[i].l1 <= m && m <= qry[i].r1 ) {
    			vl[qry[i].l1].push_back(node(qry[i].l2, qry[i].r2, qry[i].id));
    			vr[qry[i].r1].push_back(node(qry[i].l2, qry[i].r2, qry[i].id));
    			flag = true;
    		}
    	t1.maintain1(1, rt1[m]);
    	for(int j=0;j<(int)vr[m].size();j++) {
    		node t = vr[m][j];
    		ans[t.x] = max(ans[t.x], t1.query(1, t.l, t.r));
    	}
    	for(int i=m+1;i<=r;i++) {
    		for(int j=0;j<(int)v1[i].size();j++) {
    			node t = v1[i][j];
    			t1.modify(1, t.l, t.r, t.x);
    		}
    		for(int j=0;j<(int)vr[i].size();j++) {
    			node t = vr[i][j];
    			ans[t.x] = max(ans[t.x], t1.query(1, t.l, t.r));
    		}
    	}
    	t2.maintain1(1, rt2[m]);
    	for(int j=0;j<(int)vl[m].size();j++) {
    		node t = vl[m][j];
    		ans[t.x] = max(ans[t.x], t2.query(1, t.l, t.r));
    	}
    	for(int i=m-1;i>=l;i--) {
    		for(int j=0;j<(int)v2[i].size();j++) {
    			node t = v2[i][j];
    			t2.modify(1, t.l, t.r, t.x);
    		}
    		for(int j=0;j<(int)vl[i].size();j++) {
    			node t = vl[i][j];
    			ans[t.x] = max(ans[t.x], t2.query(1, t.l, t.r));
    		}
    	}
    	
    	int p = 0;
    	for(int i=1;i<=k;i++)
    		if( qry[i].r1 < m )
    			swap(qry[++p], qry[i]);
    	solve(l, m - 1, p);
    	p = 0;
    	for(int i=1;i<=k;i++)
    		if( qry[i].l1 > m )
    			swap(qry[++p], qry[i]);
    	solve(m + 1, r, p);
    }
    
    int n, m, q;
    
    int read() {
    	int x = 0; char ch = getchar();
    	while( ch > '9' || ch < '0' ) ch = getchar();
    	while( '0' <= ch && ch <= '9' ) x = 10*x + ch - '0', ch = getchar();
    	return x;
    }
    void write(ll x) {
    	if( !x ) return ;
    	write(x / 10), putchar(x % 10 + '0');
    }
    query arr[MAXN + 5];
    int main() {
    //	freopen("data.in", "r", stdin);
    //	freopen("data.out", "w", stdout);
    	n = read(), m = read(), q = read();
    	for(int i=1;i<=m;i++) {
    		arr[i].l1 = read(), arr[i].l2 = read();
    		arr[i].r1 = read(), arr[i].r2 = read(), arr[i].id = read();
    		v1[arr[i].r1+1].push_back(node(arr[i].l2, arr[i].r2, -arr[i].id));
    		v2[arr[i].l1-1].push_back(node(arr[i].l2, arr[i].r2, -arr[i].id));
    	}
    	for(int i=1;i<=m;i++) {
    		v1[arr[i].l1].push_back(node(arr[i].l2, arr[i].r2, arr[i].id));
    		v2[arr[i].r1].push_back(node(arr[i].l2, arr[i].r2, arr[i].id));
    	}
    	rt1[0] = T1.NIL, rt2[n + 1] = T2.NIL;
    	for(int i=1;i<=n;i++) {
    		rt1[i] = rt1[i-1];
    		for(int j=0;j<(int)v1[i].size();j++) {
    			node t = v1[i][j];
    			rt1[i] = T1.modify(rt1[i], 1, n, t.l, t.r, t.x);
    		}
    	}
    	for(int i=n;i>=1;i--) {
    		rt2[i] = rt2[i+1];
    		for(int j=0;j<(int)v2[i].size();j++) {
    			node t = v2[i][j];
    			rt2[i] = T2.modify(rt2[i], 1, n, t.l, t.r, t.x);
    		}
    	}
    	for(int i=1;i<=q;i++)
    		qry[i].l1 = read(), qry[i].l2 = read(), qry[i].r1 = read(), qry[i].r2 = read(), qry[i].id = i;
    	t1.build(1, 1, n), t2.build(1, 1, n), solve(1, n, q);
    	for(int i=1;i<=q;i++) {
    		if( ans[i] == 0 ) puts("0");
    		else write(ans[i]), puts("");
    	}
    }
    

    @details@

    被喊去打洛谷月赛,因为是第一次打有一点小紧张。

    紧张到把 div1D 看成了 div1C 然后在那里瞎搞半天结果赛后一小时才 AC = =。

  • 相关阅读:
    80端口被NT kernel & System 占用pid= 4的解决方法
    黑马程序员:装饰类的作用——增强被装饰类的方法的功能(利用组合实现复用)
    黑马程序员——java基础之文件复制
    10进制转换16进制原理及取得16进制最后一位或倒数第二位
    Django-admin源码流程
    Django-内置Admin
    Django-Form 补充
    有时间的时候可以看看
    编辑器KindEditor的使用
    Git的使用
  • 原文地址:https://www.cnblogs.com/Tiw-Air-OAO/p/12319208.html
Copyright © 2011-2022 走看看