Blog of RuSun

\begin {array}{c} \mathfrak {One Problem Is Difficult} \\\\ \mathfrak {Because You Don't Know} \\\\ \mathfrak {Why It Is Diffucult} \end {array}

P1972 [SDOI2009]HH的项链

P1972 [SDOI2009]HH的项链

莫队的做法很显然,但是数据加强后过不了。

查看代码
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
#include <iostream>
#include <cstdio>
#include <cmath>
#include <algorithm>
using namespace std;
const int N = 1e6 + 10;
int n, m, len, w[N], ans[N], cnt[N];
struct Query
{
int i, l, r;
}q[N];
int get (int x)
{
return x / len;
}
bool cmp (Query x, Query y)
{
int i = get(x.l), j = get(y.l);
return (i < j) || (i == j && x.r < y.r);
}
void add (int x, int &res)
{
if (!cnt[x])
res++;
cnt[x]++;
}
void del (int x, int &res)
{
cnt[x]--;
if (!cnt[x])
res--;
}
int main ()
{
cin >> n;
for (int i = 1; i <= n; i++)
cin >> w[i];
cin >> m ;
len = sqrt((double) n * n / m);
for (int i = 0; i < m; i++)
{
cin >> q[i].l >> q[i].r;
q[i].i = i;
}
sort(q, q + m, cmp);
for (int k = 0, i = 0, j = 1, res = 0; k < m; k++)
{
int l = q[k].l, r= q[k].r;
while (i < r)
add(w[++i], res);
while (i > r)
del(w[i--], res);
while (j < l)
del(w[j++], res);
while (j > l)
add(w[--j], res);
ans[q[k].i] = res;
}
for (int i = 0; i < m; i++)
cout << ans[i] << endl;
return 0;
}

考虑其他做法。

对于主席树直接维护序列值,发现区间是否存在一个数不具有可减性,于是不能直接做。

考虑多个重复出现的数只计算一次答案,记 $pre _ i$ 表示上一个 $i$ 出现的位置,特别地,如果这是整个序列第一次出现 $i$ ,那么 $pre _ i = 0$ 。

那么对于区间 $[l, r]$ ,如果 $pre _ i < l$​ ,那么计算答案,可以用主席树做了。

查看代码
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
#include <cstdio>
using namespace std;
template <class Type>
void read(Type &x)
{
char c;
bool flag = false;
while ((c = getchar()) < '0' || c > '9')
c == '-' && (flag = true);
x = c - '0';
while ((c = getchar()) >= '0' && c <= '9')
x = (x << 3) + (x << 1) + c - '0';
flag && (x = ~x + 1);
}
template <class Type>
void write(Type x)
{
x < 0 && (putchar('-'), x = ~x + 1);
x > 9 && (write(x / 10), 0);
putchar(x % 10 + '0');
}
const int N = 1e6 + 10, M = 3e7 + 10;
struct Node
{
int l, r, s;
} tr[M];
int n, m, pre[N], idx, rt[N];
void pushup (int x)
{
tr[x].s = tr[tr[x].l].s + tr[tr[x].r].s;
}
void modify (int &x, int t, int l = 0, int r = n - 1)
{
tr[++idx] = tr[x];
x = idx;
if (l == r)
return tr[x].s++, void();
int mid = l + r >> 1;
t <= mid ? modify(tr[x].l, t, l, mid) : modify(tr[x].r, t, mid + 1, r);
pushup(x);
}
int query (int x, int y, int t, int l = 0, int r = n - 1)
{
if (l > t)
return 0;
if (r <= t)
return tr[y].s - tr[x].s;
int mid = l + r >> 1;
return query(tr[x].l, tr[y].l, t, l, mid) + query(tr[x].r, tr[y].r, t, mid + 1, r);
}
int main ()
{
read(n);
for (int i = 1, a; i <= n; i++)
{
read(a);
modify(rt[i] = rt[i - 1], pre[a]);
pre[a] = i;
}
read(m);
for (int l, r; m; m--)
{
read(l), read(r);
write(query(rt[l - 1], rt[r], l - 1)), puts("");
}
return 0;
}

另外,区间 $[l, r]$ 有多少个数小于 $l$ ,有离线扫描线的做法。

将所有 $pre _ i$ 的 $i$ 记录在一起,从小到大添加所有 $i$ 。

每次添加后,将所有 $l = i$ 的询问查询答案。

需要一个数据结构单点修改,区间查询,显然用树状数组。

查看代码
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
#include <cstdio>
#include <vector>
using namespace std;
template <class Type>
void read(Type &x)
{
char c;
bool flag = false;
while ((c = getchar()) < '0' || c > '9')
c == '-' && (flag = true);
x = c - '0';
while ((c = getchar()) >= '0' && c <= '9')
x = (x << 3) + (x << 1) + c - '0';
flag && (x = ~x + 1);
}
template <class Type>
void write(Type x)
{
x < 0 && (putchar('-'), x = ~x + 1);
x > 9 && (write(x / 10), 0);
putchar(x % 10 + '0');
}
const int N = 1e6 + 10;
int n, m, pre[N], tr[N], ans[N];
struct Query
{
int r, id;
};
vector <Query> q[N];
vector <int> g[N];
void modify (int x)
{
for (; x <= n; x += x & -x)
tr[x]++;
}
int query (int x)
{
int res = 0;
for (; x; x -= x & -x)
res += tr[x];
return res;
}
int main ()
{
read(n);
for (int i = 1, a; i <= n; i++)
{
read(a);
g[pre[a]].push_back(i);
pre[a] = i;
}
read(m);
for (int i = 1, l, r; i <= m; i++)
{
read(l), read(r);
q[l].push_back((Query){r, i});
}
for (int i = 1; i <= n; i++)
{
for (int j : g[i - 1])
modify(j);
for (Query j : q[i])
ans[j.id] = query(j.r) - query(i - 1);
}
for (int i = 1; i <= m; i++)
write(ans[i]), puts("");
return 0;
}