题意
给你一个长度为n的数组,每个数字代表一种颜色,有两种操作:
1 问你在区间L,R中有多少种颜色,
2 把第i个位置的颜色变为C
思路:
记录一个数组pre[i]表示i这个位置的颜色往左推到的最近的相同颜色的位置是哪里,那么对于一次查询来说,如果pre[i] < L 就表示离他最近的颜色不再L区间里,之后我们可以分块儿,对于块内元素排序二分找,块外元素暴力去找不理解的看
https://blog.csdn.net/wjmwsgj/article/details/81176804 第二题
代码
#pragma GCC optimize("O3")
#pragma G++ optimize("O3"
#include <bits/stdc++.h>
using namespace std;
const int maxn = 10000 + 10;
int v[maxn] , pos[maxn] , sz , n , m , b[maxn] ,pre[maxn];
int last[1000001];
void reset(int x)
{
int l = (x - 1) * sz + 1, r = min(sz * x, n);
for (int i = l; i <= r;i++) pre[i] = b[i];
sort(pre+l,pre+r+1);
}
int find(int x,int v)
{
int l=(x-1)*sz+1,r=min(x*sz,n);
int first=l;
while(l<=r)
{
int mid=(l+r)>>1;
if(pre[mid]<v)l=mid+1;
else r=mid-1;
}
return l-first;
}
void build()
{
for (int i = 1; i <= n;i++)
{
pos[i] = (i - 1) / sz + 1;
b[i] = last[v[i]];
last[v[i]] = i;
}
for (int i = 1; i <= pos[n]; i++) reset(i);
}
int query(int l,int r)
{
int ans = 0;
for(int i = l ; i <= min(r,pos[l]*sz) ; i++) if(b[i] < l) ans++;
if(pos[l] != pos[r])
{
for(int i = (pos[r] - 1) * sz + 1 ; i <= r ; i++) if(b[i] < l) ans ++;
}
for (int i = pos[l] + 1; i <= pos[r] - 1;i++) ans += find(i, l);
return ans;
}
void update(int p,int c)
{
for (int i = 1; i <= n;i++) last[v[i]] = 0;
v[p] = c;
for (int i = 1; i <= n;i++)
{
int t = b[i];
b[i] = last[v[i]];
//for(int j = 1 ; j <= n ; j++) printf("%d %d\n",last[v[j]] ,b[j]);puts("~~~~`");
if (t != b[i]) reset(pos[i]);
last[v[i]] = i;
}
}
int main()
{
scanf("%d%d", &n, &m);
{
sz = sqrt(n);
for (int i = 1; i <= n;i++) scanf("%d", &v[i]);
build();
char sh[10];
int x, y;
for (int i = 1; i <= m;i++)
{
scanf("%s%d%d",sh, &x, &y);
if(sh[0] == 'Q') printf("%d\n", query(x, y));
else update(x, y);
}
}
}