【洛谷P1903】【BZOJ2120】数颜色 / 维护队列【带修莫队】
题目大意:
题目链接:
洛谷:https://www.luogu.org/problemnew/show/P1903
BZOJ:https://www.lydsy.com/JudgeOnline/problem.php?id=2120
只画笔,维护以下操作:
- 代表询问你从第支画笔到第支画笔中共有几种不同颜色的画笔。
- 把第支画笔替换为颜色。
思路:
一开始看到这题兴奋的敲了一个分块,敲到最后才发现而不是。。。
正解是带修莫队。
如果没有修改操作,那么这就是莫队的板子。
只要加上修改就可以了。
其实莫队的修改也是暴力。
我们把每一个修改记录下来,然后把每一个询问前所需要的修改数也记录下来。如果询问比询问多了一些修改,那么就把这些修改改好,如果少了就改回去。
其实真的就是暴力。
void work(int x,int i) //要做第x次修改,这次是询问i
{
if (chg[x].x>=ask[i].l&&chg[x].x<=ask[i].r) //在区间内修改才有贡献
{
cnt[a[chg[x].x]]--;
if (!cnt[a[chg[x].x]]) sum--;
if (!cnt[chg[x].val]) sum++;
cnt[chg[x].val]++;
}
swap(a[chg[x].x],chg[x].val);
//交换之后,如果下次要改回来时就相当于把现在的值储存到原数组里
}
带修莫队平均最有复杂度大约是取。证明是不可能证明的XD
不知道为什么我敲的莫队跑的贼慢。不加的话要跑。请大佬斧正。
代码:
%:pragma GCC optimize("Ofast")
%:pragma GCC optimize("inline")
#include <cmath>
#include <cstdio>
#include <string>
#include <algorithm>
#define rr register
using namespace std;
const int N=50010,M=1000010;
int n,m,s,Q,T,l,r,sum,k,a[N],ans[N],cnt[M],pos[N];
char ch;
int read()
{
int d=0;
char ch=getchar();
while (!isdigit(ch)) ch=getchar();
while (isdigit(ch))
d=(d<<3)+(d<<1)+ch-48,ch=getchar();
return d;
}
void write(int x)
{
if (x>9) write(x/10);
putchar(x%10+48);
}
struct Ask
{
int l,r,id,k;
}ask[N];
struct Change
{
int x,val;
}chg[N];
bool cmp(Ask x,Ask y)
{
if (pos[x.l]<pos[y.l]) return 1;
if (pos[x.l]>pos[y.l]) return 0;
return x.r<y.r;
}
void del(int x)
{
cnt[a[x]]--;
if (!cnt[a[x]]) sum--;
}
void add(int x)
{
if (!cnt[a[x]]) sum++;
cnt[a[x]]++;
}
void work(int x,int i)
{
if (chg[x].x>=ask[i].l&&chg[x].x<=ask[i].r)
{
cnt[a[chg[x].x]]--;
if (!cnt[a[chg[x].x]]) sum--;
if (!cnt[chg[x].val]) sum++;
cnt[chg[x].val]++;
}
swap(a[chg[x].x],chg[x].val);
}
int main()
{
n=read(); Q=read();
T=pow((double)n,(1.0/3.0));
for (rr int i=1;i<=n;i++)
{
a[i]=read();
pos[i]=(i-1)/T+1;
}
for (rr int i=1;i<=Q;i++)
{
while (ch=getchar()) if (ch=='Q'||ch=='R') break;
if (ch=='Q')
{
m++;
ask[m].l=read(); ask[m].r=read();
ask[m].id=m; ask[m].k=s;
}
else
{
s++;
chg[s].x=read(); chg[s].val=read();
}
}
sort(ask+1,ask+1+m,cmp);
l=1;
for (rr int i=1;i<=m;i++)
{
for (;l<ask[i].l;l++) del(l);
for (;l>ask[i].l;l--) add(l-1);
for (;r<ask[i].r;r++) add(r+1);
for (;r>ask[i].r;r--) del(r);
for (;k<ask[i].k;k++) work(k+1,i);
for (;k>ask[i].k;k--) work(k,i);
ans[ask[i].id]=sum;
}
for (rr int i=1;i<=m;i++)
write(ans[i]),putchar(10);
return 0;
}