【JZOJ 4594】【UVa 12345】Dynamic len
题意:
两种操作: M x y 将 a[x]的值修改为 y ,Q x y 求区间 [x,y-1] 有几个不相同的数字。注意题目中的下标从0开始。
输入的时候我们从下标 1 开始,修改时需要先将 坐标加1 ,查询时的区间为 [++x , y]
#include<iostream>
#include<algorithm>
#include<cstdlib>
#include<sstream>
#include<cstring>
#include<bitset>
#include<cstdio>
#include<string>
#include<deque>
#include<stack>
#include<cmath>
#include<queue>
#include<set>
#include<map>
using namespace std;
typedef long long ll;
const int maxn = 1e6+10;
struct node
{
int l,r;
int time;
int id;
}t[maxn];
struct Change
{
int last; //此次修改时原来的值,用于还原数组
int x; //修改位置
int val; //修改为那个值
}c[maxn];
int n,m,a[maxn];
int block;
int numq,numc; //查询、修改操作的次数
ll ans,cnt[maxn],res[maxn],last[maxn],flag[maxn];
bool cmp(node a,node b){//排序
if( (a.l/block)==(b.l/block) ){//当左端点位于同一个块时
if( (a.r/block)==(b.r/block) )//当右端点位于同一个块时
return a.time<b.time;
else
return a.r<b.r;
}
else//当左端点不位于同一个块时
return a.l<b.l;
//return (a.l/block)^(b.l/block) ? a.l<b.l : ( ((a.r/block)^(b.r/block))?a.r<b.r:a.time<b.time );
}
void add(int x) //统计新的
{
if(cnt[a[x]]==0) //如果这个点没出现过
ans++;
cnt[a[x]]++;
flag[x] = 1; //标记该点在记录中
}
void del(int x) //减去旧的
{
cnt[a[x]]--;
if(cnt[a[x]]==0) //这个点只出现过一次
ans--;
flag[x] = 0; //标记该点不在记录中
}
void push(int x) //如果这个点原本有,那就删掉,没有就加上
{
if(flag[x]) //这个操作是因为后面有反向操作
del(x);
else
add(x);
}
void change(int x,int v)
{
if(flag[x]) // 改变这个点的值,如果原本有值,删掉后赋值,没有就直接赋值
{
del(x);
a[x] = v;
add(x);
}
else
a[x] = v;
}
int main()
{
while(~scanf("%d%d",&n,&m))
{
ans = 0;
numq = numc = 0;
memset(cnt,0,sizeof(cnt));
block = pow(n,0.66666);
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
last[i] = a[i];
}
for(int i=1;i<=m;i++)
{
char ch[10];
scanf("%s",ch);
if(ch[0]=='Q') //查询
{
numq++; //查询操作次数+1
scanf("%d%d",&t[numq].l,&t[numq].r);
t[numq].l++;
t[numq].id = numq;
t[numq].time = numc;
}
else //修改
{
numc++; //修改操作次数+1
scanf("%d%d",&c[numc].x,&c[numc].val);
c[numc].x++;
c[numc].last = last[c[numc].x]; //由于修改的值可能要修改回去,所以要记录修改前的值
last[c[numc].x] = c[numc].val;
}
}
sort(t+1,t+1+numq,cmp); //对询问进行排序
int l=1,r=0,ti=0; //左右指针与时间轴
for(int i=1;i<=numq;i++)
{
while(l>t[i].l) push(--l); //[l-1,r,time]
while(l<t[i].l) push(l++); //[l+1,r,time]
while(r<t[i].r) push(++r); //[l,r+1,time]
while(r>t[i].r) push(r--); //[l,r-1,time]
while(ti<t[i].time) ti++,change(c[ti].x,c[ti].val); //[l,r,time+1] 把这个询问时间前的修改全部做了
while(ti>t[i].time) change(c[ti].x,c[ti].last),ti--; //[l,r,time-1] 若是询问在当前时间后面,那么就要把修改了的改回去
res[t[i].id] = ans;
}
for(int i=1;i<=numq;i++)
printf("%lld\n",res[i]);
}
return 0;
}