ICPC南昌网络赛I.Yukino With Subinterval

题意:

对一个序列有两种操作:

  • $1,pos,v,将a_{pos}改成v$
  • $2, l, r, x,y,查询[l,r]区间内,连续数字的段有几段,求数字大小在[x,y]间(比如1,2,2,5,6,就有4段为1,(2,2),5,6,在[2,5]就只有2段)$

这题如果提前说了可以用$cdq分治-三维偏序来做$,那就很好理解,但是网络赛中绝大部分使用树套树,就被卡常数,为了巩固下$cdq$就来做了

其实从第二个询问就可以看出来有两个范围之类的东西,不难想到二维偏序,以位置$pos$为$x$轴,以数值$a_{pos}$为$y$轴,这样查询是否就是询问坐标轴以$(l,x)$左下角,$(r,y)$右上角的矩阵中直线的个数,然后我们把直线转换为点,就是个二维偏序问题。

  • 需要注意的是,如果第a[l]是连续直线中的一个点,在累计答案时需要+1

再来看修改,也就是可以说就是一个三维偏序的魔改,我们修改时需要规定4点

  • 如果这个元素不属于连续的第一个元素,那就不用删除这个点。反之还需要将他前面的点更新成第一个点。
  • 如果新添加元素属于连续的第一个元素,那么要新建这个点。反之,还有将他前面的点删除。
代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
#include <iostream>
#include <queue>
#include <algorithm>
#include <cstdio>
using namespace std;
typedef long long ll;
#define lowbit(i) i &(-i)
const int N = 2e6 + 10;
int tr[N], n, cnt;
void update(int x, int w)
{
while (x <= n)
{
tr[x] += w;
x += lowbit(x);
}
return;
}
int quary(int x)
{
int sum = 0;
while (x)
{
sum += tr[x];
x -= lowbit(x);
}
return sum;
}
struct node
{
int x, y;
int ti, flag, w;
int pp;
} p[N * 5];
void addpoint(int x, int y, int ti, int w, int flag)
{
p[++cnt].x = x;
p[cnt].y = y;
p[cnt].ti = ti;
p[cnt].w = w;
p[cnt].flag = flag;
}
bool cmpx(node a, node b)
{
return a.x == b.x ? a.y < b.y : a.x < b.x;
}
int a[N];
int ans[N], m;
void cdq(int l, int r)
{
if (l == r)
return;
int mid = (l + r) >> 1;
cdq(l, mid);
cdq(mid + 1, r);
sort(p + l, p + mid + 1, cmpx);
sort(p + mid + 1, p + r + 1, cmpx);
int pl = l, pr = mid + 1;
while (pr <= r)
{
while (pr <= r && p[pr].flag == 0)
pr++;
while (pl <= mid && p[pl].flag == 1)
pl++;
if (pl <= mid && p[pl].x <= p[pr].x)
update(p[pl].y, p[pl].w), pl++;
else if (pr <= r)
ans[p[pr].ti] += p[pr].w * quary(p[pr].y), pr++;
}
for (int i = l; i < pl; i++)
if (p[i].flag == 0)
update(p[i].y, -p[i].w);
}
int tt;
int main()
{
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; i++)
scanf("%d", &a[i]);
for (int i = 1; i <= n; i++)
if (a[i] != a[i - 1])
addpoint(i, a[i], 0, 1, 0);

for (int i = 1; i <= m; i++)
{
int p, x, v, x1, x2, y1, y2;
scanf("%d", &p);
if (p == 1)
{
scanf("%d%d", &x, &v);
if (x != n && a[x] == a[x + 1])
addpoint(x + 1, a[x + 1], i, 1, 0);
if (x == 1 || a[x] != a[x - 1])
addpoint(x, a[x], i, -1, 0);
a[x] = v;
if (x != n && a[x] == a[x + 1])
addpoint(x + 1, a[x + 1], i, -1, 0);
if (x == 1 || a[x] != a[x - 1])
addpoint(x, a[x], i, 1, 0);
}
if (p == 2)
{
tt++;
scanf("%d%d%d%d", &x1, &x2, &y1, &y2);
if (x1 > 1 && a[x1] == a[x1 - 1] && a[x1] <= y2 && a[x1] >= y1)
ans[tt]++;
addpoint(x2, y2, tt, 1, 1);
addpoint(x1 - 1, y1 - 1, tt, 1, 1);
addpoint(x1 - 1, y2, tt, -1, 1);
addpoint(x2, y1 - 1, tt, -1, 1);
}
}

cdq(1, cnt);

for (int i = 1; i <= tt; i++)
printf("%d\n", ans[i]);
}