这道题的大体题意就是给你N个数,对于这连续的N个数有两种操作,第一种操作是将某一种颜色改成另一种颜色,而第二种就是
询问从L到R的范围内颜色段(几个连续的且颜色都相同的点算作1个颜色段)的数目。
所以我们可以使用线段树加上启发式合并(对于要合并的两个区间,把小的区间合成到大的区间里,可以上网搜索一下),这样就
可以保证时间复杂度为 n*log(n)*log(n),这样的时间复杂度是可以接受的!具体看代码。
#include
#include
#include
#include
using namespace std;
const int maxn = 1e6 + 7;
struct node{ /// 线段树,分别需要维护的是这个区间的左右端点的位置,这段区间内的颜色段数与及左右端点的颜色
int l, r, lc, rc, val;
} a[maxn<<2];
struct Item{ /// 链式前向星存点
int data, next;
} keke[maxn];
int cnt, color[maxn], sum[maxn], record[maxn];
void init(){ /// 初始化
memset(color, -1, sizeof( color));
memset(sum, 0, sizeof( sum));
for(int i = 1; i <= maxn-7; i ++) record[i] = i;
cnt = 1;
}
void add(int w, int pos){ /// 前向星增加
keke[cnt].data = pos;
keke[cnt].next = color[w];
color[w] = cnt ++;
sum[w] ++;
}
void Buildtree(int key, int l, int r){ /// 建线段树
a[key].l = l;
a[key].r = r;
if(l == r){
int tt;
scanf("%d", &tt);
a[key].lc = a[key].rc = tt;
a[key].val = 1;
add(tt, l);
return;
}
int mid = (l + r) >> 1;
Buildtree(key<<1, l, mid);
Buildtree(key<<1|1, mid + 1, r);
a[key].val = a[key<<1].val + a[key<<1|1].val; /// 合并区间,维护区间内的颜色段数
if(a[key<<1].rc == a[key<<1|1].lc) a[key].val --; /// 判断合并区间时左区间的右端点颜色和右区间左端点的颜色是否相同
a[key].lc = a[key<<1].lc;
a[key].rc = a[key<<1|1].rc;
}
node Query(int key, int l, int r){ /// 查询函数
if(a[key].l == l && a[key].r == r) return a[key];
int mid = (a[key].l + a[key].r) >> 1;
if(r <= mid) return Query(key<<1, l, r);
else if(l > mid) return Query(key<<1|1, l, r);
else{ /// 和上面合并区间的方法雷同
node t1 = Query(key<<1, l, mid), t2 = Query(key<<1|1, mid + 1, r);
t1.val += t2.val;
if(t1.rc == t2.lc) t1.val --;
t1.r = t2.r;
t1.rc = t2.rc;
return t1;
}
}
void update(int key, int w, int pos){ /// 单点更新
if(a[key].l == a[key].r && a[key].l == pos){
a[key].lc = a[key].rc = w;
a[key].val = 1;
return;
}
int mid = (a[key].l + a[key].r) >> 1;
if(pos <= mid) update(key<<1, w, pos);
else update(key<<1|1, w, pos);
a[key].val = a[key<<1].val + a[key<<1|1].val; /// 单点更新后对于区间的维护
if(a[key<<1].rc == a[key<<1|1].lc) a[key].val --;
a[key].lc = a[key<<1].lc;
a[key].rc = a[key<<1|1].rc;
}
void Merge(int p, int q){ /// 合并区间,这里有的是启发式合并
int temp = color[p];
for( ; keke[temp].next != -1; temp = keke[temp].next){
update(1, record[q], keke[temp].data); /// 每次都暴力更新点
}
update(1, record[q], keke[temp].data);
keke[temp].next = color[q];
sum[p] += sum[q];
color[q] = -1;
sum[q] = 0;
swap(record[p], record[q]);
}
//void Show(int key){
// printf("%d %d %d %d %d %d\n", key, a[key].l, a[key].r, a[key].lc, a[key].rc, a[key].val);
// if(a[key].l == a[key].r) return;
// Show(key<<1);
// Show(key<<1|1);
//}
int main(){
int t, n, m, u, v, z;
scanf("%d", &t);
while(t --){
scanf("%d %d", &n, &m);
init();
Buildtree(1, 1, n);
while(m --){
scanf("%d %d %d", &z, &u, &v);
if(z == 1){
if(u == v) continue;
if(sum[u] && sum[v]){
if(sum[u] < sum[v]) Merge(u, v);
else Merge(v, u);
}
if(sum[u] > sum[v]){
swap(sum[u], sum[v]);
swap(color[u], color[v]);
swap(record[u], record[v]);
}
}
else printf("%d\n", Query(1, u, v).val);
}
}
return 0;
}