2120: 数颜色
Time Limit: 6 Sec Memory Limit: 259 MB
Submit: 8602 Solved: 3557
Description
墨墨购买了一套N支彩色画笔(其中有些颜色可能相同),摆成一排,你需要回答墨墨的提问。墨墨会像你发布如下指令: 1、 Q L R代表询问你从第L支画笔到第R支画笔中共有几种不同颜色的画笔。 2、 R P Col 把第P支画笔替换为颜色Col。为了满足墨墨的要求,你知道你需要干什么了吗?
Input
第1行两个整数N,M,分别代表初始画笔的数量以及墨墨会做的事情的个数。第2行N个整数,分别代表初始画笔排中第i支画笔的颜色。第3行到第2+M行,每行分别代表墨墨会做的一件事情,格式见题干部分。
Output
对于每一个Query的询问,你需要在对应的行中给出一个数字,代表第L支画笔到第R支画笔中共有几种不同颜色的画笔。
Sample Input
6 5
1 2 3 4 5 5
Q 1 4
Q 2 6
R 1 2
Q 1 4
Q 2 6
Sample Output
4
4
3
4
HINT
对于100%的数据,N≤10000,M≤10000,修改操作不多于1000次,所有的输入数据中出现的所有整数均大于等于1且不超过10^6。
带修莫队:
#include<bits/stdc++.h>
using namespace std;
const int MAXN = 10005;
int n, m, Q, a[MAXN];
struct Query{int id, l, r, Time, ans;} q[MAXN];
struct Change{int pos, Old, New;} c[MAXN];
int block, l, r, T, ans, cnt[MAXN*100], pos[MAXN], now[MAXN];
bool cmp(Query a, Query b)
{
return pos[a.l]==pos[b.l]?(pos[a.r]==pos[b.r]?a.Time<b.Time:a.r<b.r):a.l<b.l;
}
bool cmp_id(Query A, Query B){ return A.id < B.id; }
void update(int x, int d)
{
cnt[x] += d;
if (cnt[x] == 0 && d == -1) ans--;
if (cnt[x] == 1 && d == 1) ans++;
}
void change(int x, int d)
{
if (l <= x && x <= r)
{
update(d, 1),
update(a[x], -1);
}
a[x] = d;
}
int main()
{
scanf("%d%d", &n, &m);
block = pow(n, 2.0/3);
for (int i = 1; i <= n; i++)
{
scanf("%d", &a[i]);
now[i] = a[i];
pos[i] = (i-1)/block+1;
}
while (m--)
{
char op; int x, y; scanf(" %c %d%d", &op, &x, &y);
if (op == 'Q') q[Q++] = Query{Q, x, y, T};
if (op == 'R') c[++T] = Change{x, now[x], y}, now[x] = y;
}
sort(q, q+Q, cmp);
l = 1, r = 0, T = 0, ans = 0;
for (int i = 0; i < Q; i++)
{
//由于函数参数的执行顺序的问题++和--的位置和顺序一定不能错
while (T < q[i].Time) change(c[T].pos, c[++T].New);//T先++,然后传给两个参数
while (T > q[i].Time) change(c[T--].pos, c[T].Old);//先传给两个参数然后T--
while (l < q[i].l) update(a[l++], -1);
while (l > q[i].l) update(a[--l], 1);
while (r < q[i].r) update(a[++r], 1);
while (r > q[i].r) update(a[r--], -1);
q[i].ans = ans;
}
sort(q, q+Q, cmp_id);
for (int i = 0; i < Q; i++) printf("%d\n", q[i].ans);
return 0;
}
/*
6 5
1 2 3 4 5 5
Q 1 4
Q 2 6
R 1 2
Q 1 4
Q 2 6
*/
分块:
#include <bits/stdc++.h>
using namespace std;
const int N = 5e4+10, M = 1e6+10, INF = 0x3f3f3f3f;
inline int read()
{
char ch = getchar(); int x = 0, f = 1;
while(ch < '0' || ch > '9') {if(ch == '-') f = -1; ch = getchar();}
while('0' <= ch && ch <= '9') {x = x * 10 + ch - '0'; ch = getchar();}
return x * f;
}
int n, m, block, sz, pos[N], L[N], R[N], pre[N], last[M], a[N], b[N];
char ch[10];
void reset(int x)
{
for(int i = L[x]; i <= R[x]; i++) pre[i] = b[i];
sort(pre + L[x], pre + R[x] + 1);
}
void build()
{
block = 0.5 * sqrt(n);
sz = n / block;
if(n % block) sz++;
for(int i = 1; i <= n; i++) pos[i] = (i-1) / block + 1;
for(int i = 1; i <= sz; i++)
{
L[i] = (i-1) * block + 1;
R[i] = i * block;
}
R[sz] = n;
memset(last, 0, sizeof last);
for(int i = 1; i <= n; i++)
{
b[i] = last[a[i]];
last[a[i]] = i;
}
for(int i = 1; i <= sz; i++) reset(i);
}
void update(int x, int v) //直接更新所有点的前驱节点
{
for(int i = 1; i <= n; i++) last[a[i]] = 0;
a[x] = v;
for(int i = 1; i <= n; i++)
{
int tmp = b[i];
b[i] = last[a[i]];
if(tmp != b[i]) reset(pos[i]);
last[a[i]] = i;
}
}
int query(int x, int y)
{
int t1 = pos[x], t2 = pos[y];
int ans = 0;
for(int i = x; i <= min(R[t1], y); i++)
if(b[i] < x) ans++;
if(t1 != t2) for(int i = L[t2]; i <= y; i++)
if(b[i] < x) ans++;
for(int i = t1 + 1; i < t2; i++)
ans += lower_bound(pre + L[i], pre + R[i] + 1, x) - (pre + L[i]);
return ans;
}
int main()
{
n = read(); m = read();
for(int i = 1; i <= n; i++) a[i] = read();
build();
int x, y;
for(int i = 1; i <= m; i++)
{
scanf("%s",&ch); x = read(); y = read();
if(ch[0] == 'Q') printf("%d\n", query(x, y));
else update(x, y);
}
return 0;
}