题目链接
好久以前写的东西了==,现在已经记不起来当时线段树咋写的了
于是
Y
Y
YY
YY出一个
C
D
Q
CDQ
CDQ的做法
好像还没人发过
C
D
Q
CDQ
CDQ的题解?抢个fb
首先先把给的序列预处理一把
记
L
[
i
]
L[i]
L[i]为
i
i
i左边小等
a
[
i
]
a[i]
a[i]的位置
+
1
+1
+1
同理有
R
[
i
]
R[i]
R[i]为
i
i
i右边小等
a
[
i
]
a[i]
a[i]的位置
−
1
-1
−1
然后上一把二维数点叫我二维数点大师
显然可以发现,一个以
(
L
[
i
]
,
i
)
(L[i],i)
(L[i],i)为左下角的,以
(
i
,
R
[
i
]
)
(i,R[i])
(i,R[i])为右上角的矩形会贡献
a
[
i
]
a[i]
a[i]的答案(也就是说,这个矩形里全是
a
[
i
]
a[i]
a[i]
然后询问就相当于查询一个以
(
l
,
l
)
(l,l)
(l,l)为左下角的,以
(
r
,
r
)
(r,r)
(r,r)为右上角的矩形和
考虑操作和询问都具有前缀性,于是可以把一个矩形拆成四个点
具体来说,把操作
(
x
l
,
x
r
,
y
l
,
y
r
)
(xl,xr,yl,yr)
(xl,xr,yl,yr)拆成
(
x
l
,
y
l
)
,
(
x
l
,
y
r
+
1
)
,
(
x
r
+
1
,
y
l
)
,
(
x
r
+
1
,
y
r
+
1
)
(xl,yl),(xl,yr + 1),(xr + 1,yl),(xr + 1,yr + 1)
(xl,yl),(xl,yr+1),(xr+1,yl),(xr+1,yr+1)
把询问
(
x
l
,
x
r
,
y
l
,
y
r
)
(xl,xr,yl,yr)
(xl,xr,yl,yr)拆成
(
x
r
,
y
r
)
,
(
x
l
−
1
,
y
r
)
,
(
x
r
,
y
l
−
1
)
,
(
x
l
−
1
,
y
l
−
1
)
(xr,yr),(xl - 1,yr),(xr,yl - 1),(xl - 1,yl - 1)
(xr,yr),(xl−1,yr),(xr,yl−1),(xl−1,yl−1)
然后填填正负号
答案即为询问和操作的四个点两两贡献的和
当然直接计算两两贡献是
O
(
n
2
)
O(n ^ 2)
O(n2)的
考虑
C
D
Q
CDQ
CDQ分治
就是先把询问和操作都按 x x x排序,然后考虑计算完贡献后归并 y y y上来
然后考虑一件事情:如何计算贡献?
如果说直接计算贡献的话,就是
v
a
l
[
i
]
∗
v
a
l
[
j
]
.
v
a
l
∗
a
b
s
(
x
[
i
]
−
x
[
j
]
+
1
)
∗
a
b
s
(
y
[
i
]
−
y
[
j
]
+
1
)
val[i] * val[j].val * abs(x[i] - x[j] + 1) * abs(y[i] - y[j] + 1)
val[i]∗val[j].val∗abs(x[i]−x[j]+1)∗abs(y[i]−y[j]+1)(也就是把以
i
i
i和
j
j
j形成的矩形大小乘以权值,其中
i
i
i为操作,
j
j
j为询问,
v
a
l
val
val是填上正负号的权值)
当然这样直接做是不行的,考虑怎么利用
x
[
i
]
<
x
[
j
]
x[i] < x[j]
x[i]<x[j]并且
y
y
y有序的性质
考虑某个
j
j
j点计算完答案长啥样子:
然后我们现在要把他变成
类似这样的形状
记一个
l
e
n
len
len和
w
i
d
wid
wid,分别表示
i
1
,
i
2
,
i
3
i_1,i_2,i_3
i1,i2,i3到
j
j
j的长和宽的带权和
具体而言就是
(大概是这个意思,只不过要带权
那我们每次先把原来的矩形向左向右扩一下,再向上向下扩一下,就可以了
用
l
e
n
len
len和
w
i
d
wid
wid表现出来就是
n
o
w
+
=
w
i
d
∗
(
x
[
i
]
−
x
[
j
−
1
]
)
;
l
e
n
+
=
s
u
m
∗
(
x
[
j
]
−
x
[
j
−
1
]
)
;
now += wid * (x[i] - x[j - 1]); len += sum * (x[j] - x[j - 1]);
now+=wid∗(x[i]−x[j−1]);len+=sum∗(x[j]−x[j−1]);
n
o
w
+
=
l
e
n
∗
(
y
[
i
]
−
y
[
j
−
1
]
)
;
w
i
d
+
=
s
u
m
∗
(
y
[
i
]
−
y
[
j
−
1
]
)
;
now += len * (y[i] - y[j - 1]); wid += sum * (y[i] - y[j - 1]);
now+=len∗(y[i]−y[j−1]);wid+=sum∗(y[i]−y[j−1]);
s
u
m
sum
sum表示当前所有点的权值和
当然可能会出现新的点(也就是原来不在
j
j
j的范围内,现在出现在
j
+
1
j + 1
j+1的范围内了),那么这个直接加起来就好了
最后要开 _ i n t 128 \_int128 _int128,当然因为博主比较懒, l o n g l o n g long\ long long long交了一个 80 p t s 80pts 80pts的就没再管了
代码( 80 p t s 80pts 80pts)
#include<cstdio>
#include<cmath>
#include<queue>
#include<stack>
#include<vector>
#include<algorithm>
#include<cstring>
using namespace std;
typedef long long LL;
const int maxn = 1000010;
struct node{
int x,y,val,typ,id;
}q[maxn],tmp[maxn];
int n,m,N,L[maxn],R[maxn],a[maxn];
int stk[maxn],top;
LL ans[maxn];
inline LL getint()
{
LL ret = 0,f = 1;
char c = getchar();
while (c < '0' || c > '9')
{
if (c == '-') f = -1;
c = getchar();
}
while (c >= '0' && c <= '9')
ret = ret * 10 + c - '0',c = getchar();
return ret * f;
}
inline int cmp(node a,node b)
{
return a.x < b.x || (a.x == b.x && a.y < b.y) || (a.x == b.x && a.y == b.y && a.typ < b.typ);
}
inline void solve(int l,int r)
{
if (l == r) return;
int mid = l + r >> 1;
solve(l,mid); solve(mid + 1,r);
int j = l,pre = 0; LL now = 0,len = 0,wid = 0,cnt = 0;
for (int i = mid + 1; i <= r; i++)
{
if (!q[i].typ) continue;
now += wid * (q[i].x - q[pre].x); len += 1ll * cnt * (q[i].x - q[pre].x);
now += len * (q[i].y - q[pre].y); wid += 1ll * cnt * (q[i].y - q[pre].y);
pre = i;
while (j <= mid && q[j].y <= q[i].y)
{
if (q[j].typ) {j++; continue;}
len += 1ll * q[j].val * (q[i].x - q[j].x + 1);
wid += 1ll * q[j].val * (q[i].y - q[j].y + 1);
now += 1ll * q[j].val * (q[i].x - q[j].x + 1) * (q[i].y - q[j].y + 1);
cnt += q[j].val; j++;
}
ans[q[i].id] += q[i].val * now;
}
int u = l,v = mid + 1,cur = l - 1;
while (u <= mid && v <= r)
{
if (q[u].y <= q[v].y) tmp[++cur] = q[u++];
else tmp[++cur] = q[v++];
}
for (int i = u; i <= mid; i++) tmp[++cur] = q[i];
for (int i = v; i <= r; i++) tmp[++cur] = q[i];
for (int i = l; i <= r; i++) q[i] = tmp[i];
}
inline void split(int xl,int xr,int yl,int yr,int typ,int id)
{
if (typ == 0)
{
q[++N] = (node){xl,yl,a[id],0,id};
q[++N] = (node){xl,yr + 1,-a[id],0,id};
q[++N] = (node){xr + 1,yl,-a[id],0,id};
q[++N] = (node){xr + 1,yr + 1,a[id],0,id};
}
else
{
q[++N] = (node){xr,yr,1,1,id};
q[++N] = (node){xl - 1,yr,-1,1,id};
q[++N] = (node){xr,yl - 1,-1,1,id};
q[++N] = (node){xl - 1,yl - 1,1,1,id};
}
}
int main()
{
#ifdef AMC
freopen("AMC1.txt","r",stdin);
freopen("AMC2.txt","w",stdout);
#endif
n = getint(); m = getint();
for (int i = 1; i <= n; i++) a[i] = getint();
for (int i = 1; i <= n; i++)
{
while (top && a[stk[top]] > a[i]) top--;
L[i] = stk[top] + 1;
stk[++top] = i;
}
top = 0;
for (int i = n; i >= 1; i--)
{
while (top && a[stk[top]] > a[i]) top--;
if (top) R[i] = stk[top] - 1; else R[i] = n;
stk[++top] = i;
}
for (int i = 1; i <= n; i++)
split(L[i],i,i,R[i],0,i);
for (int i = 1; i <= m; i++)
{
int l = getint(),r = getint();
split(l,r,l,r,1,i);
}
sort(q + 1,q + N + 1,cmp);
solve(1,N);
for (int i = 1; i <= m; i++)
printf("%lld\n",ans[i]);
return 0;
}
非常好写并且短233333333