题意:给出一个长度为n的数组,数组里的元素是[1,1000000]中的整数,代表一个颜色,两个操作,一个操作是把数组中的某个颜色换成另一个颜色,还有一个操作是求数组中某区间连续整数段的个数。
思路:
线段树和树状数组都可以操作。
线段树的思路:记下一个整数所有对应区间,存放在这个整数对应的数组中,每次修改一个颜色,就把这个颜色对应所有区间的颜色修改(线段树的update操作)。其他差不多是基本线段树操作,自己为了方便计数,令query操作返回一个结构体,详见第一份代码。
复杂度分析:很容易构造出这样的数据,数组偶数位置是颜色A,奇数位置是颜色B,第一个操作是把颜色A变为颜色B,之后的操作是把B变A,A变B,不难想到这样数据的时间复杂度是O( q*n*logn ),只不过oj上没有这样的数据,不会卡掉这样的代码。
树状数组的思路:考虑用树状数组维护每一个位置是否为一段颜色的起点(下简称“起点”)。 询问时,只需要查询区间内起点个数,再特判左端点是否为起点,即可求得答案。对合并操作,题解里说采用“启发式合并”,就是把小的颜色合并到大的颜色中,但这样势必涉及到了颜色的交换,因此还需要一个数组来记录每个颜色其代表的真正颜色。一开始自己用并查集的想法来做,然后就有了问题,后来反应过来,这道题里面没有过多的从属关系,只是为了方便用一个颜色来表示另一个颜色而已。具体操作就是用类似邻接表的数据结构存下每个颜色对应数组位置的下标,每次修改其对应的树状数组位置。详见第二份代码(这份代码参考了:
http://blog.csdn.net/jtjy568805874/article/details/53727197)
复杂度分析:每次修改的位置总数最多只能是目前的最大区间长度。因此对合并操作的最坏复杂度之和是O( n*logn )。
代码:
//线段树
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;
#define MS(x,y) memset(x,y,sizeof(x))
const int MAXN=1e5+5;
const int MAXM=1e6+5;
int n,q;
int col[MAXN];
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
struct Node{
int cnt,l,r,lazy;
Node operator+(const Node& rhs) const {
Node ret;
ret.l=l;
ret.r=rhs.r;
ret.cnt=cnt+rhs.cnt;
ret.lazy=-1;
if(r==rhs.l){
--ret.cnt;
}
return ret;
}
}node[MAXN<<2],tmp;
vector<Node> colsgm[MAXM];
inline void pushup(int rt){
int l=(rt<<1),r=(rt<<1|1);
node[rt]=node[l]+node[r];
}
inline void pushdown(int rt){
if(node[rt].lazy==-1) return ;
int l=(rt<<1),r=(rt<<1|1);
node[l].cnt=node[r].cnt=1;
node[l].lazy=node[r].lazy=node[rt].lazy;
node[l].l=node[r].l=node[l].r=node[r].r=node[rt].lazy;
node[rt].lazy=-1;
}
void build(int l,int r,int rt){
if(l==r){
node[rt].l=node[rt].r=col[l];
node[rt].cnt=1;
node[rt].lazy=-1;
return ;
}
int m=(l+r)>>1;
build(lson);
build(rson);
pushup(rt);
}
void update(int x,int L,int R,int l,int r,int rt){
if(L<=l&&r<=R){
node[rt].l=node[rt].r=x;
node[rt].cnt=1;
node[rt].lazy=x;
return ;
}
pushdown(rt);
int m=(l+r)>>1;
if(L<=m) update(x,L,R,lson);
if(m<R) update(x,L,R,rson);
pushup(rt);
}
Node query(int L,int R,int l,int r,int rt){
if(L<=l&&r<=R){
return node[rt];
}
pushdown(rt);
int m=(l+r)>>1;
Node ret;
ret.cnt=0;
ret.l=ret.r=-1;
bool flag=false;
if(L<=m) ret=query(L,R,lson),flag=true;
if(m<R){
if(flag) ret=ret+query(L,R,rson);
else ret=query(L,R,rson);
}
return ret;
}
int main(){
int T;
scanf("%d",&T);
while(T--){
scanf("%d%d",&n,&q);
for(int i=0;i<MAXM;++i) colsgm[i].clear();
int c=-1;
for(int i=1;i<=n;++i){
scanf("%d",col+i);
if(c==-1){
c=col[i];
tmp.l=i;
}
else if(c!=col[i]){
tmp.r=i-1;
colsgm[c].push_back(tmp);
c=col[i];
tmp.l=i;
}
}
tmp.r=n;
colsgm[c].push_back(tmp);
build(1,n,1);
int op,x,y;
while(q--){
scanf("%d%d%d",&op,&x,&y);
if(op==1){
if(x==y) continue;
for(int i=colsgm[x].size()-1;i>=0;--i){
update(y,colsgm[x][i].l,colsgm[x][i].r,1,n,1);
colsgm[y].push_back(colsgm[x][i]);
colsgm[x].pop_back();
}
}
else{
printf("%d\n",query(x,y,1,n,1).cnt);
}
}
}
}
//树状数组
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
#define MS(x,y) memset(x,y,sizeof(x))
#define lowbit(x) (x&(-x))
const int MAXN=1e5+5;
const int MAXM=1e6+5;
int val[MAXN],ar[MAXN],f[MAXM],siz[MAXM];
int n;
struct Edge{
int v,nxt;
}edge[MAXN];
int head[MAXM],edgenum;
void addedge(int u,int v){
edge[edgenum].v=v;
edge[edgenum].nxt=head[u];
head[u]=edgenum++;
}
void add(int x,int v){
for(;x<MAXN;x+=lowbit(x)) ar[x]+=v;
}
int query(int x){
int ret=0;
for(;x;x-=lowbit(x)) ret+=ar[x];
return ret;
}
int main(){
int T;
scanf("%d",&T);
while(T--){
MS(head,-1);
MS(siz,0);
MS(f,0);
MS(ar,0);
edgenum=0;
int q,op,x,y;
scanf("%d%d",&n,&q);
for(int i=1;i<=n;++i){
scanf("%d",val+i);
f[val[i]]=val[i];
++siz[val[i]];
addedge(val[i],i);
if(val[i]!=val[i-1]) add(i,1);
}
while(q--){
scanf("%d%d%d",&op,&x,&y);
if(op==2){
printf("%d\n",query(y)-query(x-1)+(val[x]==val[x-1]));
}
else{
int s=x,t=y;
if(x==y||!siz[f[x]]) continue;
x=f[x];
y=f[y];
if(siz[x]>siz[y]) swap(x,y);
siz[y]+=siz[x];
siz[x]=0;
f[t]=y;
f[s]=0;
for(int i=head[x];~i;i=edge[i].nxt){
int &v=edge[i].v;
if(val[v]!=val[v-1]) add(v,-1);
if(val[v+1]!=val[v]) add(v+1,-1);
val[v]=y;
if(val[v]!=val[v-1]) add(v,1);
if(val[v+1]!=val[v]) add(v+1,1);
if(edge[i].nxt==-1){
edge[i].nxt=head[y];
head[y]=head[x];
head[x]=-1;
break;
}
}
head[x]=-1;
}
}
}
}