hdu4638-Group(莫队)

题意:求一系列编号最少能变成区间使得区间内编号连续,比如1、2、5,把1和2放在一个区间,5放在一个区间,答案是2。给n个数每个数有一个编号,m次查询L到R区间的答案。

分析:莫队处理m个查询。增加:一个数两边都没有数,加入它相当于加入了一个独立区域,答案会加一,一个数两边都有数,加入它相当于把两边联合在一起,答案会减一。删除与增加相反。需要注意这里增加和删除不能够抵消,当出现增加和删除不能够抵消的情况要先把答案清零,然后更新为当前区间的贡献,再继续操作,具体看代码实现。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 1e5+10;
int t,n,m,l,r,res,siz;
int a[maxn],pos[maxn];
int ans[maxn],cnt[maxn];
struct Q{
	int l,r,k;	
	bool friend operator<(const Q &a,const Q &b){
		return pos[a.l] == pos[b.l] ? a.r < b.r:pos[a.l] < pos[b.l];
	}
}q[maxn];
void add(int pos){
	if (!a[pos]) return;
	if (!cnt[a[pos]+1]&&!cnt[a[pos]-1]) res++;
	if (cnt[a[pos]+1]&&cnt[a[pos]-1]) res--;
	cnt[a[pos]]++;
}
void del(int pos){
	if (!a[pos]) return;
	cnt[a[pos]]--;
	if (!cnt[a[pos]+1]&&!cnt[a[pos]-1]) res--;
	if (cnt[a[pos]+1]&&cnt[a[pos]-1]) res++;
}
int main(){
	scanf("%d",&t);
	while (t--){
		memset(cnt,0,sizeof cnt);
		scanf("%d%d",&n,&m);
		siz = sqrt(n);
		for (int i = 1; i <= n; i++){
			scanf("%d",&a[i]);
			pos[i] = i/siz+1;
		}
		for (int i = 1; i <= m; i++){
			scanf("%d%d",&q[i].l,&q[i].r);
			q[i].k = i;
		}
		sort(q+1,q+m+1);
		l = 1, r = 0, res = 0;
		for (int i = 1; i <= m; i++){
			if (l > q[i].r || r < q[i].l){
				res = 0;
				while (l <= r) cnt[a[l++]] = 0;
				l = q[i].l , r = q[i].l - 1;
				while (r < q[i].r) add(++r);
				ans[q[i].k] = res;
				continue;
			}
			while (l < q[i].l) del(l++);
			while (l > q[i].l) add(--l);
			while (r < q[i].r) add(++r);
			while (r > q[i].r) del(r--);
			ans[q[i].k] = res;
		}
		for (int i = 1; i <= m; i++){
			printf("%d
",ans[i]);
		}
	}
	return 0;
}
原文地址:https://www.cnblogs.com/hznudreamer/p/12579867.html