# 带修改莫队算法

update in 2017.12.24：

## code

update in  2018.5.4

// luogu-judger-enable-o2
// luogu-judger-enable-o2
#include<cstdio>
#include<cmath>
#include<algorithm>
#define swap(x, y) x ^= y, y ^= x, x^= y
using namespace std;
const int MAXN= 2*1e6+10;
char c=getchar();int x=0,f=1;
while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
while(c>='0'&&c<='9'){x=x*10+c-'0',c=getchar();}
return x*f;
}
char obuf[1<<24], *O=obuf;
void print(int x) {
if(x > 9) print(x / 10);
*O++ = x % 10 + '0';
}
int N, M;
int a[MAXN], where[MAXN];
struct Query {
int x, y, pre, id;
}Q[MAXN];
int Qnum = 0;
struct Change {
int pos,val;
}C[MAXN];
int Cnum = 0;
int color[MAXN], ans=0, base, out[MAXN];
int comp(const Query &a,const Query &b) {
return where[a.x] == where[b.x] ? ( where[a.y] == where[b.y] ? a.pre < b.pre : a.y < b.y ): a.x < b.x ;
}
void Delet(int val){ if(--color[val]==0) ans--; }
void Work(int now, int i) {
if(C[now].pos >= Q[i].x && C[now].pos <= Q[i].y) {
if( --color[a[C[now].pos]] == 0 ) ans--;
if( ++color[C[now].val] == 1)     ans++;
}
swap(C[now].val, a[C[now].pos]);
}
void MoQueue()
{
int l = 1, r = 0, now = 0;
for(int i = 1;i <= Qnum;i++)
{
while(l < Q[i].x) Delet(a[l++]);
while(r > Q[i].y) Delet(a[r--]);
while(now < Q[i].pre) Work(++now, i);
while(now > Q[i].pre) Work(now--, i);
out[Q[i].id] = ans;
}
for(int i = 1;i <= Qnum; i++)
print(out[i]), *O++ = '\n';
}
int main()
{
#ifdef WIN32
freopen("a.in", "r", stdin);
freopen("a.out","w",stdout);
#endif
for(int i = 1;i <= N; i++) a[i] = read();
while(M--)
{
char opt[5];
scanf("%s",opt);
if(opt[0] == 'Q') {
Q[Qnum].pre = Cnum;//????????????
Q[Qnum].id = Qnum;
}
else if(opt[0] == 'R') {
}
}
base = pow(N, 0.6666666666);
for(int i = 1; i <= N; i++) where[i] = i / base + 1;
sort(Q+1, Q+Qnum+1, comp);
MoQueue();
fwrite(obuf, O-obuf, 1, stdout);
return 0;
}

#include<cstdio>
#include<cmath>
#include<algorithm>
using namespace std;
const int MAXN=2*1e6+10;
{
char c=getchar();int x=0,f=1;
while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
while(c>='0'&&c<='9'){x=x*10+c-'0',c=getchar();}
return x*f;
}
int N,M;
int a[MAXN],where[MAXN];
struct Query
{
int x,y,pre,id;
}Q[MAXN];
int Qnum=0;
struct Change
{
int pos,val;
}C[MAXN];
int Cnum=0;
int color[MAXN],ans=0,base,out[MAXN];
int comp(const Query &a,const Query &b)
{
if(a.x!=b.x) return where[a.x]<where[b.x];
if(a.y!=b.y) return where[a.y]<where[b.y];
return a.pre<b.pre;
}
{
if(++color[val]==1) ans++;
}
void Delet(int val)
{
if(--color[val]==0) ans--;
}
void Work(int now,int i)
{
if(C[now].pos>=Q[i].x&&C[now].pos<=Q[i].y)//注意：只有修改在查询的区间内才会对查询的结果产生影响
{
if( --color[a[C[now].pos]] == 0 ) ans--;
if( ++color[C[now].val] == 1)      ans++;
}
swap(C[now].val,a[C[now].pos]);
//这里有个很巧妙的操作
//对于一个操作，下一次需要为的颜色是本次被改变的颜色
//比如，我把颜色3改为了7，那么再进行这次修改的时候就是把7改为3
//所以直接交换两种颜色就好
}
void MoQueue()
{
int l=1,r=0,now=0;
for(int i=1;i<=Qnum;i++)
{
while(l<Q[i].x)    Delet(a[l++]);
while(r>Q[i].y) Delet(a[r--]);//以上四句为莫队模板
while(now<Q[i].pre) Work(++now,i);//改少了，改过去
while(now>Q[i].pre) Work(now--,i);//改多了，改回来
out[Q[i].id]=ans;//统计答案
}
for(int i=1;i<=Qnum;i++)
printf("%d\n",out[i]);
}
int main()
{
base=sqrt(N);
while(M--)
{
char opt[5];
scanf("%s",opt);
if(opt[0]=='Q')
{
Q[Qnum].pre=Cnum;//别忘了记录最近的修改位置
Q[Qnum].id=Qnum;
}
else if(opt[0]=='R')
{
}
}
sort(Q+1,Q+Qnum+1,comp);//玄学排序
MoQueue();
return 0;
}

## 复杂度证明

• 如果第$i$个询问区间和第$i+1$个询问区间的左端点所在块的编号相同，那么左端点的移动不会超过$\sqrt{n}$。

也就是说，左端点一直在块内移动的总复杂度为$O(n*\sqrt{n})$(因为左端点最多转移$n$次，减去左端点跨越块的部分，不足$n$)

同时由于右端点升序，那么若$l,l+1,,,r-1,r$的询问区间左端点所在块的编号相等，那么右端点的移动不会超过n次。有一位有$\sqrt{n}$个块，

所以这一部分的复杂度是$O(n*\sqrt{n})$的。

• 考虑左端点跨越块的情况，每次跨越最大是$O(2*\sqrt{n})$那么左端点跨越块的复杂度$O(n*\sqrt{n})$的。

又在这个期间，每次左端点跨越的时候，右端点可能要移动$O(n)$次，一共左端点跨越$\sqrt{n}$个块，所以右端点复杂度是$O(n*\sqrt{n})$的。

$t$轴移动的复杂度为$O\left( \dfrac {n^{2}t}{a^{2}}\right)$，同$r$块$l,r$移动复杂度为$0\left( na\right)$，$l$块间的$r$移动复杂度为$0\left( \dfrac {n}{a}\right)$

posted @ 2017-07-12 10:31 自为风月马前卒 阅读(...) 评论(...) 编辑 收藏

……