【题解】 「AHOI / HNOI2017」影魔 线段树+单调栈+扫描线+差分
作者:互联网
Legend
Link \(\textrm{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 \times n\) 的一个矩阵,其中位于 \((i,j)\ (i < j)\) 的数字代表右端点取 \(i\),左端点取 \(j\) 时候的贡献。
例如,这是样例的矩阵:
R\L 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\n");
}
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\n" ,ans[i]);
return 0;
}
标签:影魔,int,题解,sum,stk,add,扫描线,top,lch 来源: https://www.cnblogs.com/imakf/p/14279787.html