zoukankan      html  css  js  c++  java
  • 【题解】 「AHOI / HNOI2017」影魔 线段树+单调栈+扫描线+差分

    Legend

    Link ( extrm{to LOJ})

    Editorial

    题意翻译之后就是有两个贡献方式:

    • 区间端点为最大和次大时 (+p_1)
    • 区间端点为最大和非次大时 (+p_2)

    手玩一下就感觉这个贡献方式与单调栈出奇地像,不妨就用单调栈的角度来分析这个贡献。

    先求出每个区间的贡献是 (p_1,p_2,0) 中的哪一种。

     #
     # #
     # #          #
     # #   #      # <-New
     # #   #     ##
     # #   #     ##
    ===============
    010122212222210
    

    考虑上图这个例子,# 的个数表示数字大小,下方的数字表示以 (New) 为右端点,每个左端点的答案。

    容易发现以下几个性质:

    • 之前位于单调栈中的元素所在位置的贡献均为 (p_1)
    • 以单调栈中第一个大于 (New) 的元素为分界线,左侧不在单调栈中的位置贡献为 (0),右侧为 (p_2)

    第一个贡献非常好做:我们求出每一个元素在取哪些右端点的时候存在,这显然是一个区间 ([st,end])。算这个贡献可以直接扫描线差分。

    这部分的复杂度是 (O(n log n))

    第二个贡献也比较好做:对于单调栈中相邻两个元素,记录在取哪些右端点的时候存在,这显然也是一个区间 ([st,end]),依然扫描线解决这个贡献。

    因为单调栈每一个元素只会插入弹出一次,所以“相邻两个元素”的数量是 (O(n)) 的,复杂度也是 (O(n log n)) 的。

    这样子,我们通过扫描线可以求出一个 (n imes n) 的一个矩阵,其中位于 ((i,j) (i < j)) 的数字代表右端点取 (i),左端点取 (j) 时候的贡献。

    例如,这是样例的矩阵:

    RL 1   2   3   4   5   6   7   8   9   10
    1   0   0   0   0   0   0   0   0   0   0
    2   2   0   0   0   0   0   0   0   0   0
    3   0   2   0   0   0   0   0   0   0   0
    4   0   3   2   0   0   0   0   0   0   0
    5   0   3   2   2   0   0   0   0   0   0
    6   3   2   2   3   2   0   0   0   0   0
    7   0   0   0   0   0   2   0   0   0   0
    8   0   0   0   0   0   2   2   0   0   0
    9   0   0   0   0   0   3   0   2   0   0
    10  0   0   0   0   0   3   0   2   2   0
    

    而我们对于 ([l,r]) 的一次询问,本质就是求矩形 ((l,l)-(r,r)) 的元素和,这可以一起扫描线+差分解决。


    具体怎么差分呢?我们可以分析一下查询矩形和修改矩形的关系。

    修改矩形已经被我们在横坐标上差分了,即变成了左侧的一个 (+v) 线段和右侧的一个 (-v) 线段。

    查询矩形也被我们在横坐标上差分了,变成了右侧的前缀和减左侧的前缀和。

    现在单独考虑怎么求一个前缀和:即看修改矩形横坐标与当前查询位置的距离。

    假设修改矩形位于 (x_1),权值为 (v) ,查询位于 (x_2),则对于某一行,它的贡献就是 ((x_2-x_1+1)v=x_2v-x_1v+v)

    不难发现我们可以维护 (sum (1-x_1)v)(sum v) 的值,就可以求出来了。

    总复杂度 (O(n log n)),不过常数比较大。

    Code

    大概还是有些细节,感觉这个差分的套路写起来挺爽的

    #include <bits/stdc++.h>
    
    #define debug(...) fprintf(stderr ,__VA_ARGS__)
    #define __FILE(x)
    	freopen(#x".in" ,"r" ,stdin);
    	freopen(#x".out" ,"w" ,stdout)
    #define LL long long
    
    const int MX = 2e5 + 23;
    const LL MOD = 998244353;
    
    int read(){
    	char k = getchar(); int x = 0;
    	while(k < '0' || k > '9') k = getchar();
    	while(k >= '0' && k <= '9') x = x * 10 + k - '0' ,k = getchar();
    	return x;
    }
    
    LL n ,m ,p1 ,p2;
    int p[MX];
    
    int q;
    struct QUERY{
    	int type ,x ,l ,r;
    	LL id;
    }Q[MX * 10];
    
    bool cmp(QUERY A ,QUERY B){
    	if(A.x != B.x) return A.x < B.x;
    	return A.type < B.type;
    }
    
    struct node{
    	int l ,r;
    	LL add ,sum;
    	node *lch ,*rch;
    }*R1 ,*R2;
    
    void pushup(node *x){x->sum = x->lch->sum + x->rch->sum;}
    void doadd(node *x ,LL v){x->add += v ,x->sum += (x->r - x->l + 1) * v;}
    
    node *build(int l ,int r){
    	node *x = new node;
    	x->l = l ,x->r = r;
    	x->add = x->sum = 0;
    	if(l == r){
    		x->lch = x->rch = nullptr;
    	}
    	else{
    		int mid = (l + r) >> 1;
    		x->lch = build(l ,mid);
    		x->rch = build(mid + 1 ,r);
    		pushup(x);
    	}
    	return x;
    }
    
    void pushdown(node *x){
    	if(x->add){
    		doadd(x->lch ,x->add);
    		doadd(x->rch ,x->add);
    		x->add = 0;
    	}
    }
    
    void add(node *x ,int l ,int r , LL v){
    	if(l <= x->l && x->r <= r) return doadd(x ,v);
    	pushdown(x);
    	if(l <= x->lch->r) add(x->lch ,l ,r ,v);
    	if(r > x->lch->r) add(x->rch ,l ,r ,v);
    	return pushup(x);
    }
    
    LL sum(node *x ,int l ,int r){
    	if(l <= x->l && x->r <= r) return x->sum;
    	pushdown(x); LL s = 0;
    	if(l <= x->lch->r) s += sum(x->lch ,l ,r);
    	if(r > x->lch->r) s += sum(x->rch ,l ,r);
    	return s;
    }
    
    LL ans[MX];
    LL sgn(LL x){
    	if(x > 0) return 1;
    	if(!x) return 0;
    	return -1;
    }
    
    int S[100][100];
    int stk[MX] ,top;
    int main(){
    	n = read() ,m = read() ,p1 = read() ,p2 = read();
    	for(int i = 1 ; i <= n + 1 ; ++i){
    		if(i == 6){
    			debug("asDFASDFASDFADS
    ");
    		}
    		if(i <= n) p[i] = read();
    		else p[i] = INT_MAX;
    
    		while(top && p[stk[top]] < p[i]){
    			if(top > 1) Q[++q] = (QUERY){0 ,i ,stk[top - 1] ,stk[top - 1] ,-p2};
    			if(stk[top - 1] + 1 <= stk[top] - 1){
    				Q[++q] = (QUERY){0 ,i ,stk[top - 1] + 1 ,stk[top] - 1 ,p2};
    				Q[++q] = (QUERY){0 ,i + 1 ,stk[top - 1] + 1 ,stk[top] - 1 ,-p2};
    			}
    
    			Q[++q] = (QUERY){0 ,i ,stk[top] ,stk[top] ,p1};
    			Q[++q] = (QUERY){0 ,i + 1 ,stk[top] ,stk[top] ,-p1};
    			--top;
    		}
    		stk[++top] = i;
    		if(top > 1){
    			Q[++q] = (QUERY){0 ,i ,stk[top - 1] ,stk[top - 1] ,p1};
    			Q[++q] = (QUERY){0 ,i + 1 ,stk[top - 1] ,stk[top - 1] ,-p1};
    			Q[++q] = (QUERY){0 ,i + 1 ,stk[top - 1] ,stk[top - 1] ,p2};
    		}
    	}
    
    	for(int i = 1 ,l ,r ; i <= m ; ++i){
    		l = read() ,r = read();
    		Q[++q] = (QUERY){1 ,l - 1 ,l ,r ,-i};
    		Q[++q] = (QUERY){1 ,r ,l ,r ,i};
    	}
    
    	std::sort(Q + 1 ,Q + 1 + q ,cmp);
    
    	R1 = build(0 ,n + 3);
    	R2 = build(0 ,n + 3);
    	for(int i = 1 ; i <= q ; ++i){
    		if(Q[i].type == 0){
    			add(R1 ,Q[i].l ,Q[i].r ,Q[i].id);
    			add(R2 ,Q[i].l ,Q[i].r ,(1LL - Q[i].x) * Q[i].id);
    		}
    		else{
    			ans[std::abs(Q[i].id)] += sgn(Q[i].id) * Q[i].x * sum(R1 ,Q[i].l ,Q[i].r);
    			ans[std::abs(Q[i].id)] += sgn(Q[i].id) * sum(R2 ,Q[i].l ,Q[i].r);
    		}
    	}
    	for(int i = 1 ; i <= m ; ++i)
    		printf("%lld
    " ,ans[i]);
    	return 0;
    
    }
    
  • 相关阅读:
    Linux kernel device mapper
    草莓网
    openwrt系统源码地址
    ubuntu 安装eclipse for c++
    ubuntu下安装eclipse IDE for C/C++ developers
    Makefile 中:= ?= += =的区别
    core dump
    rtp
    skbuff
    A Neural Algorithm of Artistic Style
  • 原文地址:https://www.cnblogs.com/imakf/p/14279787.html
Copyright © 2011-2022 走看看