Link
https://www.luogu.org/problemnew/show/P3722
题意
给出
n
n
n 个整数
k
i
,
i
=
1
⋯
n
k_i,i=1\cdots n
ki,i=1⋯n 它们组成一个
1
∼
n
1\sim n
1∼n 的排列
对于点对
(
a
,
b
)
(a,b)
(a,b) 如果
max
i
∈
(
a
,
b
)
k
i
≤
max
{
k
a
,
k
b
}
\max\limits_{i\in(a,b)}k_i\le\max\{k_a,k_b\}
i∈(a,b)maxki≤max{ka,kb} 那么产生贡献
p
1
p_1
p1
如果
max
i
∈
(
a
,
b
)
k
i
∈
(
min
{
k
a
,
k
b
}
,
max
{
k
a
,
k
b
}
)
\max\limits_{i\in(a,b)} k_i\in(\min\{k_a,k_b\},\max\{k_a,k_b\})
i∈(a,b)maxki∈(min{ka,kb},max{ka,kb}) 那么产生贡献
p
2
p_2
p2
询问
m
m
m 次:每次询问一段区间
[
L
,
R
]
[L,R]
[L,R] 内点对贡献的和
1
≤
n
,
m
≤
2
×
1
0
5
,
1
≤
p
1
,
p
2
≤
1
0
3
1\le n,m\le2\times10^5,1\le p_1,p_2\le10^3
1≤n,m≤2×105,1≤p1,p2≤103
说是点对产生贡献,但是点对中间夹了一大堆东西,特别是最大值尤其突出啊。
在这里要注意一点,我们下面的考虑都是基于有“中间”的情况,所以统计贡献要强加(i,j)相邻的贡献
我们考虑每个点在哪个范围内能够作为最大值,然后计算它的贡献
这个东西你可以开一个单调栈扫两遍,也可以直接扫一遍(%%%)
对于
i
i
i 我们假设它两边第一个比它大的数位置分别是
l
i
,
r
i
l_i,r_i
li,ri
考虑离线……
① 显然
i
i
i 对
[
l
i
,
r
i
]
[l_i,r_i]
[li,ri] 贡献为
p
1
p_1
p1
② 对
[
l
i
+
1
∼
i
−
1
,
r
i
]
[l_i+1\sim i-1,r_i]
[li+1∼i−1,ri] 贡献为
p
2
p_2
p2
③ 对
[
l
i
,
i
+
1
∼
r
i
−
1
]
[l_i,i+1\sim r_i-1]
[li,i+1∼ri−1] 贡献为
p
2
p_2
p2
期望扫一遍能够得到贡献,那么应该是对每个询问统计
[
L
,
R
]
[L,R]
[L,R] 到
R
R
R 产生的贡献和减掉到
L
−
1
L-1
L−1 产生的贡献和
然后我就嘴巴AC这道题啦 嘴它!
具体要怎么维护呢?
我们考虑开一个数据结构扫 Timeline 的话……
① 那么它必须只在
l
i
∼
r
i
l_i\sim r_i
li∼ri 的时间内产生一次贡献,而且必须给且只给
[
l
i
,
r
i
]
[l_i,r_i]
[li,ri] 产生
考虑安排在
r
i
r_i
ri 产生贡献,给
l
i
l_i
li 产生然后在
l
i
+
1
l_{i+1}
li+1 消失
就杠杠好……反过来也可以,反正就是限制一下更小的区间不会统计到这个贡献即可
② 同理,为了方便在
r
i
r_i
ri 给
(
l
i
,
i
)
(l_i,i)
(li,i) 搞
③ 同理,为了方便在
l
i
l_i
li 给
(
i
,
r
i
)
(i,r_i)
(i,ri) 搞
用树壮数组或者线duang树维护同一时刻里面的{贡献(按时间的)前缀和}的前缀和就好了。
ps
壮壮数组写起来好壮壮啊
有点毒瘤
结果并没有卡常 那我干什么不用线段树秒
其实是道水题啊
我爆炸弱了(
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cstdlib>
#include<cctype>
#include<ctime>
#include<cmath>
using namespace std;
const int MAXN = 4e5;
int n, m, p1, p2, tot;
int k[MAXN], stk[MAXN];
long long c1[MAXN], c2[MAXN];
int L[MAXN], R[MAXN];
struct Opt
{
int l, r, x, id, value;
inline Opt(const int&a=0, const int&b=0, const int&c=0, const int&d=0, const int&e=0)
{
l = a, r = b, x = c, id = d, value = e;
}
inline bool operator < (const Opt& o) const
{
return x < o.x;
}
}Needle[2*MAXN], Noodle[3*MAXN];
int NeedleTot, NoodleTot;
long long Ans[MAXN];
inline long long Query(int Pos)
{
long long Ans = 0;
int Tmp = Pos; //Attention
while (Pos)
{
Ans += 1ll * (Tmp + 1) * c1[Pos] - c2[Pos];
Pos -= Pos & -Pos;
}
return Ans;
}
inline void Modify(int Pos, const int& x)
{
int Tmp = Pos; //Attention
while (Pos <= n)
{
c1[Pos] += x;
c2[Pos] += 1ll * Tmp * x;
Pos += Pos & -Pos;
}
}
int main()
{
scanf("%d%d%d%d", &n, &m, &p1, &p2);
for (register int i = 1; i <= n; ++i) scanf("%d", &k[i]);
k[0] = k[n+1] = n+1;
++stk[0];
for (register int i = 1; i <= n + 1; ++i)
{
while (k[stk[stk[0]]]<k[i]) R[stk[stk[0]]] = i, --stk[0];
L[i] = stk[stk[0]];
stk[++stk[0]] = i;
}
for (register int Lefta, Rightb, i = 1; i <= m; ++i)
{
scanf("%d%d", &Lefta, &Rightb);
Ans[i] = 1ll * (Rightb - Lefta) * p1;
if (Lefta > 1) Needle[++NeedleTot] = Opt(Lefta, Rightb, Lefta-1, i, -1);
Needle[++NeedleTot] = Opt(Lefta, Rightb, Rightb, i, 1);
}
sort(Needle + 1, Needle + 1 + NeedleTot);
for (register int i = 1; i <= n; ++i)
{
if (1 <= L[i] && R[i] <= n) Noodle[++NoodleTot] = Opt(L[i], L[i], R[i], 0, p1);
if (L[i] < i-1 && R[i] <= n) Noodle[++NoodleTot] = Opt(L[i]+1, i-1, R[i], 0, p2);
if (R[i] > i+1 && L[i] >= 1) Noodle[++NoodleTot] = Opt(i+1, R[i]-1, L[i], 0, p2);
}
sort(Noodle + 1, Noodle + NoodleTot + 1);
int PointerQuer = 1, PointerMod = 1;
for (register int Pos = 1; Pos <= n && PointerQuer <= NeedleTot; ++Pos)
{
while (PointerMod <= NoodleTot && Noodle[PointerMod].x == Pos)
{
Modify(Noodle[PointerMod].l, Noodle[PointerMod].value);
Modify(Noodle[PointerMod].r + 1, -Noodle[PointerMod].value);
++PointerMod;
}
while (PointerQuer <= NeedleTot && Needle[PointerQuer].x == Pos)
{
Ans[Needle[PointerQuer].id] += (Query(Needle[PointerQuer].r) - Query(Needle[PointerQuer].l-1)) * Needle[PointerQuer].value;
++PointerQuer;
}
}
for (register int i = 1; i <= m; ++i) printf("%lld\n", Ans[i]);
return 0;
}