嘟嘟嘟




这题真的挺神的,我是真没想出来。




洛谷的第一篇题解说的非常妙,实在是佩服。

就是我们首先预处理出对于第\(i\)个数,在\(i\)左边比第一个比\(i\)大的数\(l_i\),在\(i\)右边第一个比\(i\)大的数\(r_i\)。

这个可以用单调栈扫两边分别求出来。




然后我们考虑位于\([l_i, r_i]\)中的所有数产生的贡献:

1.\(l_i\)和\(r_i\)单独产生\(p1\)的贡献。

2.位于\([l_i + 1, i - 1]\)的数都和\(r_i\)产生\(p2\)的贡献。

3.位于\([i + 1, r_i - 1]\)的数都和\(l_i\)产生\(p2\)的贡献。

所以我们把所有的\(l_i, r_i\)排序,然后从1开始扫到\(n\),对于三种贡献:

1.遇到\(r_i\),就在对应的\(l_i\)上加\(p1\)。

2.遇到\(r_i\),就在区间\([l_i + 1, i - 1]\)加\(p2\)。

3.遇到\(l_i\),就在区间\([i + 1, r_i - 1]\)加\(p2\)。

同时询问也是离线下来排好序的,然后我们用前缀和的思想,把每一个询问\([L_i, R_i]\)拆成\(L_i - 1\)和\(R_i\),修改的同时遇到\(L_i - 1\)就查询\([L_i, R_i]\),并从\(ans_i\)中减去;遇到\(R_i\),就再查询一遍\([L_i, R_i]\),加到\(ans_i\)里去。

这也就能解释为什么\(p1\)的贡献加在\(l_i\)而不是\(r_i\)上了:因为扫到\(r_i\)的时候,查询区间的右端点一定满足\(R_i = r_i\),而这个贡献能否加上去,就是看\(L_i\)是否包含\(l_i\)。所以应该加到\(l_i\)上。




我这题之所以调了半天,是因为把当前枚举的位置和区间所在编号弄混了……

区间修改大佬们似乎都是写的树状数组,我一时想不明白,还是乖乖写线段树去了。

p.s.区间排序用vector直接存下来是真的方便

#include<cstdio>
#include<iostream>
#include<cmath>
#include<algorithm>
#include<cstring>
#include<cstdlib>
#include<cctype>
#include<vector>
#include<stack>
#include<queue>
using namespace std;
#define enter puts("")
#define space putchar(' ')
#define Mem(a, x) memset(a, x, sizeof(a))
#define In inline
typedef long long ll;
typedef double db;
const int INF = 0x3f3f3f3f;
const db eps = 1e-8;
const int maxn = 2e5 + 5;
inline ll read()
{
ll ans = 0;
char ch = getchar(), last = ' ';
while(!isdigit(ch)) last = ch, ch = getchar();
while(isdigit(ch)) ans = (ans << 1) + (ans << 3) + ch - '0', ch = getchar();
if(last == '-') ans = -ans;
return ans;
}
inline void write(ll x)
{
if(x < 0) x = -x, putchar('-');
if(x >= 10) write(x / 10);
putchar(x % 10 + '0');
} int n, m, p1, p2, a[maxn];
ll ans[maxn]; struct Node
{
int l, r;
}t[maxn << 1];
struct Node2
{
int id, flg; //0:l, 1:r; -1:L, 1:R
};
vector<Node2> v[maxn], q[maxn]; int st[maxn], top = 0;
In void init()
{
for(int i = 1; i <= n; ++i)
{
while(top && a[st[top]] < a[i]) --top;
t[i].l = st[top]; st[++top] = i;
}
st[top = 0] = n + 1;
for(int i = n; i; --i)
{
while(top && a[st[top]] < a[i]) --top;
t[i].r = st[top]; st[++top] = i;
}
for(int i = 1; i <= n; ++i)
{
v[t[i].l].push_back((Node2){i, 0});
v[t[i].r].push_back((Node2){i, 1});
}
} int l[maxn << 2], r[maxn << 2];
ll sum[maxn << 2], lzy[maxn << 2];
In void build(int L, int R, int now)
{
l[now] = L; r[now] = R;
if(L == R) return;
int mid = (L + R) >> 1;
build(L, mid, now << 1);
build(mid + 1, R, now << 1 | 1);
}
In void change(int now, int d)
{
sum[now] += 1LL * (r[now] - l[now] + 1) * d;
lzy[now] += d;
}
In void pushdown(int now)
{
if(lzy[now])
{
change(now << 1, lzy[now]), change(now << 1 | 1, lzy[now]);
lzy[now] = 0;
}
}
In void update(int L, int R, int now, int d)
{
if(L > R) return;
if(l[now] == L && r[now] == R) {change(now, d); return;}
pushdown(now);
int mid = (l[now] + r[now]) >> 1;
if(R <= mid) update(L, R, now << 1, d);
else if(L > mid) update(L, R, now << 1 | 1, d);
else update(L, mid, now << 1, d), update(mid + 1, R, now << 1 | 1, d);
sum[now] = sum[now << 1] + sum[now << 1 | 1];
}
In ll query(int L, int R, int now)
{
if(l[now] == L && r[now] == R) return sum[now];
pushdown(now);
int mid = (l[now] + r[now]) >> 1;
if(R <= mid) return query(L, R, now << 1);
else if(L > mid) return query(L, R, now << 1 | 1);
else return query(L, mid, now << 1) + query(mid + 1, R, now << 1 | 1);
} int main()
{
//freopen("ha.in", "r", stdin);
n = read(), m = read(), p1 = read(), p2 = read();
for(int i = 1; i <= n; ++i) a[i] = read();
init();
for(int i = 1; i <= m; ++i)
{
int L = read(), R = read();
t[i + n] = (Node){L, R};
q[L - 1].push_back((Node2){i, -1});
q[R].push_back((Node2){i, 1});
}
build(1, n, 1);
for(int i = 1; i <= n; ++i)
{
for(int j = 0; j < (int)v[i].size(); ++j)
{
int id = v[i][j].id, flg = v[i][j].flg;
if(!flg) update(id + 1, t[id].r - 1, 1, p2);
else
{
if(t[id].l) update(t[id].l, t[id].l, 1, p1);
update(t[id].l + 1, id - 1, 1, p2);
}
}
for(int j = 0, id; j < (int)q[i].size(); ++j)
id = q[i][j].id, ans[id] += query(t[id + n].l, t[id + n].r, 1) * q[i][j].flg;
}
for(int i = 1; i <= m; ++i)
write(ans[i] + 1LL * p1 * (t[i + n].r - t[i + n].l)), enter;
return 0;
}

[AH2017/HNOI2017]影魔的更多相关文章

  1. luogu P3722 [AH2017/HNOI2017]影魔

    传送门 我太弱了,只会乱搞,正解是不可能正解的,这辈子不可能写正解的,太蠢了又想不出什么东西,就是乱搞这种东西,才能维持得了做题这样子 考虑将询问离线,按右端点排序,并且预处理出每个位置往前面第一个大 ...

  2. 洛谷P3722 [AH2017/HNOI2017]影魔(线段树)

    题意 题目链接 Sol 题解好神仙啊qwq. 一般看到这种考虑最大值的贡献的题目不难想到单调数据结构 对于本题而言,我们可以预处理出每个位置左边第一个比他大的位置\(l_i\)以及右边第一个比他大的位 ...

  3. [AH2017/HNOI2017]影魔(主席树+单调栈)

    设\(l[i]\)为i左边第一个比i大的数的下标.\(r[i]\)为i右边第一个比i大的数的下标. 我们把\(p1,p2\)分开考虑. 当产生贡献为\(p1\)时\(i\)和\(j\)一定满足,分别为 ...

  4. [AH2017/HNOI2017] 影魔 - 线段树

    #include<bits/stdc++.h> #define maxn 200010 using namespace std; int a[maxn],st[maxn][2],top,L ...

  5. P3722 [AH2017/HNOI2017]影魔(单调栈+扫描线+线段树)

    题面传送门 首先我们把这两个贡献翻译成人话: 区间 \([l,r]\) 产生 \(p_1\) 的贡献当且仅当 \(a_l,a_r\) 分别为区间 \([l,r]\) 的最大值和次大值. 区间 \([l ...

  6. bzoj 4826: [Hnoi2017]影魔 [主席树 单调栈]

    4826: [Hnoi2017]影魔 题意:一个排列,点对\((i,j)\),\(p=max(i+1,j-1)\),若\(p<a_i,a_j\)贡献p1,若\(p\)在\(a_1,a_2\)之间 ...

  7. P3723 [AH2017/HNOI2017]礼物

    题目链接:[AH2017/HNOI2017]礼物 题意: 两个环x, y 长度都为n k可取 0 ~ n - 1      c可取任意值 求 ∑ ( x[i] - y[(i + k) % n + 1] ...

  8. 4826: [Hnoi2017]影魔

    4826: [Hnoi2017]影魔 https://lydsy.com/JudgeOnline/problem.php?id=4826 分析: 莫队+单调栈+st表. 考虑如何O(1)加入一个点,删 ...

  9. 【LG3722】[HNOI2017]影魔

    [LG3722][HNOI2017]影魔 题面 洛谷 题解 先使用单调栈求出\(i\)左边第一个比\(i\)大的位置\(lp_i\),和右边第一个比\(i\)大的位置\(rp_i\). 考虑\(i\) ...

随机推荐

  1. js的数据类型:单例模式,工厂模式,构造函数

    js数据类型 基本数据类型:string   undefined   null  boolean  number 引用数据类型  Object  array  function 二者的区别 基本数据类 ...

  2. Netty 系列八(基于 WebSocket 的简单聊天室).

    一.前言 之前写过一篇 Spring 集成 WebSocket 协议的文章 —— Spring消息之WebSocket ,所以对于 WebSocket 协议的介绍就不多说了,可以参考这篇文章.这里只做 ...

  3. hive函数应用之操作json

    1.创建表 createtable.sql中存放的创建表语句如下 create external table adt.jsontest ( appKey string comment "AP ...

  4. Java中重写与重载

    重写(覆盖):发生在子类与父类之间:方法名相同方法的个数.类型相同返回值类型小于等于父类的返回值类型 重载:发生在一个类中:方法名相同方法的个数.类型不同返回值类型可以相同也可以不相同

  5. CSS基础知识思维导图xmind

    这是我根据自己的学习笔记整理的思维导图,WEB前端的知识很多,汇总来看会比较清晰.

  6. 掌握PHP垃圾回收机制

    php的垃圾回收机制可以简单总结为 引用计数 写时复制 COW机制, 本文主要和大家分享掌握php垃圾回收机制的知识,希望能帮助到大家. 引用计数基本知识 官网的解答如下 每个php变量存在一个叫”z ...

  7. JS中的柯里化(currying)

    何为Curry化/柯里化? curry化来源与数学家 Haskell Curry的名字 (编程语言 Haskell也是以他的名字命名). 柯里化通常也称部分求值,其含义是给函数分步传递参数,每次传递参 ...

  8. loj#6436. 「PKUSC2018」神仙的游戏(生成函数)

    题意 链接 Sol 生成函数题都好神仙啊qwq 我们考虑枚举一个长度\(len\).有一个结论是如果我们按\(N - len\)的余数分类,若同一组内的全为\(0\)或全为\(1\)(?不算),那么存 ...

  9. 「Android」GreenDao

    译文 版本:greenDAO 3.2.2 官网:http://greenrobot.org/greendao/ GitHub:https://github.com/greenrobot/greenDA ...

  10. 现有项目中集成Flutter

    本文列举了项目开发使用Flutter会遇到的问题,以及如何使用Flutter module在现有项目中集成Flutter,并对其原理进行了分析. 最近在做的一个商业项目,完全的使用Flutter编写的 ...