题目链接
墨墨购买了一套 N 支彩色画笔(其中有些颜色可能相同),摆成一排,你需要回答墨墨的提问。
墨墨会像你发布如下指令:
Q L R
代表询问你从第 L 支画笔到第 R 支画笔中共有几种不同颜色的画笔。
R P Col
把第 P 支画笔替换为颜色 Col。
为了满足墨墨的要求,你知道你需要干什么了吗?
输入格式
第 1 行两个整数 N,M,分别代表初始画笔的数量以及墨墨会做的事情的个数。
第 2 行 N 个整数,分别代表初始画笔排中第 i 支画笔的颜色。
第 3 行到第 2+M 行,每行分别代表墨墨会做的一件事情,格式见题干部分。
输出格式
对于每一个 Query
的询问,你需要在对应的行中给出一个数字,代表第 L 支画笔到第 R 支画笔中共有几种不同颜色的画笔。
数据范围
1≤N,M≤10000,
修改操作不多于 1000 次,
所有的输入数据中出现的所有整数均大于等于 1 且不超过 106。
输入样例:
输出样例:
解题思路
带修改的莫队
莫队算法是暴力离线算法,一般不支持修改操作,如果需要修改操作,则需要在原基础上增加一个维度,即 (l,r,t),将区间分块,设其长度为 len,则每一个时间戳有 n/len 块长度为 len 的块,同理每次先将 l,r 两个指针移到询问端点上,再将 t 指针暴力移到对应时间戳即可
复杂度分析:分别考虑三个指针的总时间复杂度,l 指针分为块内和块间的复杂度,块内复杂度为 O(m×len),块间距离为 O(len),l 指针块间移动最多 O(n/len) 次,块间复杂度为 O(len×n/len),故其复杂度为 O(m×len+n)≈O(m×len),r 指针也同样考虑,块内复杂度为 O(m×len),r 指针每个块块间移动的距离为 O(n),共 n/len 块,故块间复杂度为 O(n2/len),故其复杂度为 O(m×len+n2/len),设每次 t 指针移动的最长距离为 t,由于 t 指针在 l 和 r 指针固定在某一块时其复杂度为 O(t),l 和 r 指针形成的块共有 O(n/len×n/len) 块,故其复杂度为 O(t×n2/len2),由于 n 和 m 一个规模,故复杂度替换如下:
l:O(n×len)r:O(n×len+n2/len)t:O(t×n2/len2)
对于 r 指针,设 n×len>n2/len,即 len>√n,而对于 t 指针,要求 len 越大越好,故 len>√n 成立,则 r 指针的复杂度也为 O(n×len),令 n×len=t×n2/len2,得 len=3√nt,则:
代码
#include <bits/stdc++.h>
#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;
}
__EOF__
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!
2021-10-07 2020 CCPC Wannafly Winter Camp Day6 Div.1&2