###Description
有n个数编号从0→n-1,两种操作:
Q L R:询问编号为L→R-1的数中共有多少种不同的数
M X Y:将编号为X的数改为Y
共有m个操作
###Solution
把这题改对的过程我印象深刻。
我好想是拍了10次,一共拍了200+个数据,终于AC。
然后我看看别人的做法,代码量全都2000-,而我3000+。
好,不扯那么多了。
首先这题暴力可以拿30分。(废话)
然后我们考虑分块,当然这里从0开始十分的坑,要特别注意。
如何查询和修改呢?我们开两个数组 n e x t next next和 l a s t last last, n e x t i next_i nexti表示下一个与当前位置的值相等的位置, l a s t i last_i lasti则相反。
这样,如果询问的 ( l , r ) (l,r) (l,r)这个区间(注意是这个区间,是 L → R − 1 L→R-1 L→R−1这个区间为 ( l , r ) (l,r) (l,r)),如果 n e x t i > r ( l ≤ i ≤ r ) next_i>r(l≤i≤r) nexti>r(l≤i≤r)那么它的下一个相等的值都不在这个区间,可以直接统计答案。
然后,对于区间没有完全覆盖一个块的部分,直接暴力查询,对于覆盖整个块的部分,我们对每个块以 n e x t i next_i nexti为关键字排序,得到一个 n e x t i next_i nexti单调不减的块,然后我们对于这些块,二分一个最大的 p p p使得 a p ≤ r a_p≤r ap≤r,然后显然这个块 p + 1 p+1 p+1后的都是满足 n e x t i > r next_i>r nexti>r,直接统计进答案即可。
简单来说就是下图:
如果你看到这觉得懵逼了,你可以去打带修改莫队算法(简单,粗暴,250)。
这就结束了吗?
我们还有修改操作!!怎么办?!
修改的操作一样简单。我们只要改变上一次相等值的 n e x t next next,还有下一次相等值的 l a s t last last,然后把新的值加进来的 n e x t next next, l a s t last last修改,然后重新对有修改的块重新排序,就可以了。
怎么找一个新的值的 n e x t next next, l a s t last last?
我们开一个数组 S i , j S_{i,j} Si,j表示第 i i i个块, j j j这个数出现了多少次。
于是我们发现要离散化,直接按顺序标号即可。
还有一个与该题类似的题:
###Code
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<cstring>
#include<cmath>
#define fo(i,j,k) for(int i=j;i<=k;i++)
#define fd(i,j,k) for(int i=j;i>=k;i--)
#define N 50001
#define M 1000001
#define _N 250
using namespace std;
int a[N];
int map[M];
int tot=0;
struct node{
int l,r;
int z[_N],px[_N];
}b[_N];
bool bz[N*2];
int zz[N*2];
int next[N],last[N];
int s[_N][N*2];
int pos[N];
bool cmp(int x,int y)
{
return next[x]<next[y];
}
int main()
{
freopen("1942.in","r",stdin);
freopen("1942.out","w",stdout);
int n,m;
cin>>n>>m;
fo(i,0,n-1)
{
scanf("%d",&a[i]);
if(!map[a[i]]) map[a[i]]=++tot;
a[i]=map[a[i]];
}
memset(zz,-1,sizeof(zz));
fo(i,0,n-1)
{
next[i]=n;
last[i]=i;
if(zz[a[i]]<0) zz[a[i]]=i;
else
{
last[i]=zz[a[i]];
next[last[i]]=i;
zz[a[i]]=i;
}
}
int t=int(sqrt(n));
int l=0,r=t-1;
int mxn=n/t+1;
fo(i,1,n/t+1)
{
b[i].l=l,b[i].r=r;
int cnt=0;
fo(j,l,r)
{
pos[j]=i;
s[i][a[j]]++;
b[i].z[++cnt]=b[i].px[cnt]=j;
}
sort(b[i].px+1,b[i].px+r-l+2,cmp);
l=r+1;
r=min(l+t-1,n-1);
}
while(m--)
{
char ch[5];
int x,y;
scanf("%s %d %d",ch,&x,&y);
if(ch[0]=='Q')
{
x--;y--;
int ans=0;
int l=pos[x],r=pos[y];
if(l==r)
{
fo(i,x,y)
if(next[i]>y) ans++;
printf("%d\n",ans);
continue;
}
if(x>b[l].l)
{
fo(i,x,b[l].r)
if(next[i]>y) ans++;
l++;
}
if(y<b[r].r)
{
fo(i,b[r].l,y)
if(next[i]>y) ans++;
r--;
}
fo(i,l,r)
{
int ll=0,rr=b[i].r-b[i].l+1;
while(ll<rr-1)
{
int mid=(ll+rr)/2;
if(next[b[i].px[mid]]<=y) ll=mid;
else rr=mid;
}
int q;
if(next[b[i].px[rr]]<=y) q=rr;
else q=ll;
ans+=b[i].r-b[i].l-q+1;
}
printf("%d\n",ans);
}
else
{
x--;
if(!map[y]) map[y]=++tot;
y=map[y];
next[last[x]]=next[x];
int tt=pos[last[x]];
sort(b[tt].px+1,b[tt].px+b[tt].r-b[tt].l+2,cmp);
if(last[x]==x) last[next[x]]=next[x];
else last[next[x]]=last[x];
int l=pos[x],r=pos[x];
s[l][a[x]]--;
next[x]=n;
last[x]=x;
bool tf=false;
fd(i,x-1,b[l].l)
if(a[b[l].z[i-b[l].l+1]]==y)
{
last[x]=i;
next[i]=x;
tf=true;
break;
}
if(!tf)
{
fd(i,l-1,1)
if(s[i][y])
{
fd(j,b[i].r,b[i].l)
if(a[b[i].z[j-b[i].l+1]]==y)
{
last[x]=j;
next[j]=x;
sort(b[i].px+1,b[i].px+b[i].r-b[i].l+2,cmp);
tf=true;
break;
}
if(tf) break;
}
}
tf=false;
fo(i,x+1,b[l].r)
if(a[b[l].z[i-b[l].l+1]]==y)
{
next[x]=i;
last[i]=x;
tf=true;
break;
}
if(!tf)
{
fo(i,r+1,mxn)
if(s[i][y])
{
fo(j,b[i].l,b[i].r)
if(a[b[i].z[j-b[i].l+1]]==y)
{
next[x]=j;
last[j]=x;
tf=true;
break;
}
if(tf) break;
}
}
a[x]=y;
s[l][y]++;
sort(b[l].px+1,b[l].px+b[l].r-b[l].l+2,cmp);
}
}
}