2521. 数颜色
题目链接
2521. 数颜色
墨墨购买了一套 \(N\) 支彩色画笔(其中有些颜色可能相同),摆成一排,你需要回答墨墨的提问。
墨墨会像你发布如下指令:
Q L R
代表询问你从第 \(L\) 支画笔到第 \(R\) 支画笔中共有几种不同颜色的画笔。R P Col
把第 \(P\) 支画笔替换为颜色 \(Col\)。
为了满足墨墨的要求,你知道你需要干什么了吗?
输入格式
第 \(1\) 行两个整数 \(N,M\),分别代表初始画笔的数量以及墨墨会做的事情的个数。
第 \(2\) 行 \(N\) 个整数,分别代表初始画笔排中第 \(i\) 支画笔的颜色。
第 \(3\) 行到第 \(2+M\) 行,每行分别代表墨墨会做的一件事情,格式见题干部分。
输出格式
对于每一个 Query
的询问,你需要在对应的行中给出一个数字,代表第 \(L\) 支画笔到第 \(R\) 支画笔中共有几种不同颜色的画笔。
数据范围
\(1 \le N,M \le 10000\),
修改操作不多于 \(1000\) 次,
所有的输入数据中出现的所有整数均大于等于 \(1\) 且不超过 \(10^6\)。
输入样例:
6 5
1 2 3 4 5 5
Q 1 4
Q 2 6
R 1 2
Q 1 4
Q 2 6
输出样例:
4
4
3
4
解题思路
带修改的莫队
莫队算法是暴力离线算法,一般不支持修改操作,如果需要修改操作,则需要在原基础上增加一个维度,即 \((l,r,t)\),将区间分块,设其长度为 \(len\),则每一个时间戳有 \(n/len\) 块长度为 \(len\) 的块,同理每次先将 \(l,r\) 两个指针移到询问端点上,再将 \(t\) 指针暴力移到对应时间戳即可
复杂度分析:分别考虑三个指针的总时间复杂度,\(l\) 指针分为块内和块间的复杂度,块内复杂度为 \(O(m\times len)\),块间距离为 \(O(len)\),\(l\) 指针块间移动最多 \(O(n/len)\) 次,块间复杂度为 \(O(len\times n/len)\),故其复杂度为 \(O(m\times len+n)\approx O(m\times len)\),\(r\) 指针也同样考虑,块内复杂度为 \(O(m\times len)\),\(r\) 指针每个块块间移动的距离为 \(O(n)\),共 \(n/len\) 块,故块间复杂度为 \(O(n^2/len)\),故其复杂度为 \(O(m\times len+n^2/len)\),设每次 \(t\) 指针移动的最长距离为 \(t\),由于 \(t\) 指针在 \(l\) 和 \(r\) 指针固定在某一块时其复杂度为 \(O(t)\),\(l\) 和 \(r\) 指针形成的块共有 \(O(n/len\times n/len)\) 块,故其复杂度为 \(O(t\times n^2/len^2)\),由于 \(n\) 和 \(m\) 一个规模,故复杂度替换如下:
对于 \(r\) 指针,设 \(n\times len>n^2/len\),即 \(len>\sqrt{n}\),而对于 \(t\) 指针,要求 \(len\) 越大越好,故 \(len>\sqrt{n}\) 成立,则 \(r\) 指针的复杂度也为 \(O(n\times len)\),令 \(n\times len=t\times n^2/len^2\),得 \(len=^3\sqrt{nt}\),则:
- 时间复杂度:\(O(^3\sqrt{n^4t})\)
代码
// Problem: 数颜色
// Contest: AcWing
// URL: https://www.acwing.com/problem/content/2523/
// Memory Limit: 64 MB
// Time Limit: 1000 ms
//
// Powered by CP Editor (https://cpeditor.org)
// %%%Skyqwq
#include <bits/stdc++.h>
//#define int long long
#define help {cin.tie(NULL); cout.tie(NULL);}
#define pb push_back
#define fi first
#define se second
#define mkp make_pair
using namespace std;
typedef long long LL;
typedef pair<int, int> PII;
typedef pair<LL, LL> PLL;
template <typename T> bool chkMax(T &x, T y) { return (y > x) ? x = y, 1 : 0; }
template <typename T> bool chkMin(T &x, T y) { return (y < x) ? x = y, 1 : 0; }
template <typename T> void inline read(T &x) {
int f = 1; x = 0; char s = getchar();
while (s < '0' || s > '9') { if (s == '-') f = -1; s = getchar(); }
while (s <= '9' && s >= '0') x = x * 10 + (s ^ 48), s = getchar();
x *= f;
}
const int N=10005,M=1e6+5;
int n,m,cnt[M],mc,mq,w[N],len,ans[N];
struct query
{
int id,l,r,t;
}q[N];
struct modify
{
int p,v;
}c[N];
int get(int x)
{
return x/len;
}
void add(int x,int &res)
{
if(!cnt[x])res++;
cnt[x]++;
}
void del(int x,int &res)
{
cnt[x]--;
if(!cnt[x])res--;
}
bool cmp(const query& a, const query& b)
{
int al = get(a.l), ar = get(a.r);
int bl = get(b.l), br = get(b.r);
if (al != bl) return al < bl;
if (ar != br) return ar < br;
return a.t < b.t;
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)scanf("%d",&w[i]);
for(int i=1;i<=m;i++)
{
char op[2];
int l,r;
scanf("%s%d%d",op,&l,&r);
if(*op=='Q')++mq,q[mq]={mq,l,r,mc};
else
mc++,c[mc]={l,r};
}
len=cbrt((double)n*max(mc,1))+1;
sort(q+1,q+1+mq,[](const query &a,const query &b){
int al=get(a.l),ar=get(a.r),bl=get(b.l),br=get(b.r);
if(al!=bl)return al<bl;
if(ar!=br)return ar<br;
return a.t<b.t;
});
for(int i=0,j=1,t=0,res=0,k=1;k<=mq;k++)
{
int l=q[k].l,r=q[k].r,tm=q[k].t,id=q[k].id;
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);
while(t<tm)
{
t++;
if(j<=c[t].p&&c[t].p<=i)
{
del(w[c[t].p],res);
add(c[t].v,res);
}
swap(w[c[t].p],c[t].v);
}
while(t>tm)
{
if(j<=c[t].p&&c[t].p<=i)
{
del(w[c[t].p],res);
add(c[t].v,res);
}
swap(w[c[t].p],c[t].v);
t--;
}
ans[id]=res;
}
for(int i=1;i<=mq;i++)printf("%d\n",ans[i]);
return 0;
}