• 【HNOI2017】影魔


    题面

    题解

    对于两个位置(l, r),如果它们分别是区间([l, r])的最大值,那么可以产生(p1)的贡献,

    否则如果它们中有一个是最大值,那么可以产生(p2)的贡献。

    所以对于当前位置(i),假设左右两边第一个比它大的是(l, r),那么([l, r])可以产生p1的贡献,([l + 1 sim i - 1, r])([l, i + 1sim r - 1])会产生(p2)的贡献。

    于是加完(R[i])后,对(L[i])产生(p1)的贡献

    加完位置(L[i])后,对((i, R[i]))产生(p2)的贡献

    加完位置(R[i])后,对((L[i], i))产生(p2)的贡献

    那么对于一个询问(L, R),只要考虑它范围内的贡献,于是也拆成三个部分

    加入完(L - 1)时,要忽略前面(L, R)位置产生的所有贡献

    加入完(R)时,要考虑所有的(L, R)位置产生的贡献

    还有每一组的([i, i + 1])产生的贡献(p1)

    这样的话,用扫描线+区间加法+区间求和即可。

    用单调栈和树状数组就行了。

    代码

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #define RG register
    #define file(x) freopen(#x".in", "r", stdin), freopen(#x".out", "w", stdout)
    #define clear(x, y) memset(x, y, sizeof(x))
    
    namespace IO
    {
    	const int BUFSIZE = 1 << 20;
    	char ibuf[BUFSIZE], *is = ibuf, *it = ibuf;
    	inline char getchar() { if (is == it) it = (is = ibuf) + fread(ibuf, 1, BUFSIZE, stdin); return *is++; }
    }
    
    inline int read()
    {
    	int data = 0, w = 1;
    	char ch = IO::getchar();
    	while(ch != '-' && (ch < '0' || ch > '9')) ch = IO::getchar();
    	if(ch == '-') w = -1, ch = IO::getchar();
    	while(ch >= '0' && ch <= '9') data = data * 10 + (ch ^ 48), ch = IO::getchar();
    	return data * w;
    }
    
    const int maxn(2e5 + 10);
    int n, m, p1, p2, stk[maxn], top, cnt, tot;
    int L[maxn], R[maxn], a[maxn];
    long long c1[maxn], c2[maxn], ans[maxn];
    
    struct upd { int x, l, r, v; } p[maxn << 2];
    struct qry { int x, l, r, v, id; } q[maxn << 2];
    inline int operator < (const upd &a, const upd &b) { return a.x < b.x; }
    inline int operator < (const qry &a, const qry &b) { return a.x < b.x; }
    
    void add(int x, int w)
    {
    	for(RG int i = x; i <= n; i += i & -i)
    		c1[i] += w, c2[i] += x * w;
    }
    
    long long query(int x)
    {
    	long long ans = 0;
    	for(RG int i = x; i; i -= i & -i)
    		ans += (x + 1) * c1[i] - c2[i];
    	return ans;
    }
    
    int main()
    {
    #ifndef ONLINE_JUDGE
    	file(cpp);
    #endif
    	n = read(), m = read(), p1 = read(), p2 = read();
    	for(RG int i = 1; i <= n; i++) a[i] = read();
    	stk[top = 0] = 0;
    	for(RG int i = 1; i <= n; i++)
    	{
    		while(top && a[stk[top]] < a[i]) --top;
    		L[i] = stk[top], stk[++top] = i;
    	}
    
    	stk[top = 0] = n + 1;
    	for(RG int i = n; i; i--)
    	{
    		while(top && a[stk[top]] < a[i]) --top;
    		R[i] = stk[top], stk[++top] = i;
    	}
    
    	for(RG int i = 1; i <= m; i++)
    	{
    		int l = read(), r = read(); ans[i] = (r - l) * p1;
    		q[++cnt] = (qry) {r, l, r, 1, i};
    		q[++cnt] = (qry) {l - 1, l, r, -1, i};
    	}
    	std::sort(q + 1, q + cnt + 1);
    	for(RG int i = 1; i <= n; i++)
    	{
    		if(L[i] && R[i] < n + 1) p[++tot] = (upd) {R[i], L[i], L[i], p1};
    		if(L[i] && R[i] > i + 1) p[++tot] = (upd) {L[i], i + 1, R[i] - 1, p2};
    		if(L[i] + 1 < i && R[i] < n + 1)
    			p[++tot] = (upd) {R[i], L[i] + 1, i - 1, p2};
    	}
    	std::sort(p + 1, p + tot + 1);
    	for(RG int i = 1, j = 1; i <= cnt; i++)
    	{
    		while(j <= tot && p[j].x <= q[i].x)
    			add(p[j].l, p[j].v), add(p[j].r + 1, -p[j].v), ++j;
    		ans[q[i].id] += q[i].v * (query(q[i].r) - query(q[i].l - 1));
    	}
    	for(RG int i = 1; i <= m; i++) printf("%lld
    ", ans[i]);
    	return 0;
    }
    
  • 相关阅读:
    CodeForces 687B Remainders Game
    CodeForces 689D Friends and Subsequences
    CSU 1810 Reverse
    生成树收录
    吃奶酪
    带逆向思维的并查集
    中位数定理
    种类并查集(关押犯人)
    带权并查集
    分层图
  • 原文地址:https://www.cnblogs.com/cj-xxz/p/10435269.html
Copyright © 2020-2023  润新知