莫队复杂度+例题

复杂度分析

设块的大小为blocksize,序列总长为n,有m次询问,t次修改。
(1)l的移动:若下一个询问与当前询问在同一个块中则最多移动blocksize步。若下一个询问与当前询问不在同一块中则最多移动n步且移动n步后一定在一个块中。故最多移动 O ( m ∗ b l o c k s i z e + n ) O(m*blocksize+n) O(mblocksize+n)
(2)r的移动:枚举完一个块,r最多移动n次,总共有n/blocksize块,复杂度为 O ( n ∗ n / b l o c k s i z e ) O(n*n/blocksize) O(nn/blocksize)
(3)t的移动:我们要寻找有多少个单调段(一个单调段下来最多移动t次)。当且仅当两个询问l在同块,r也在同块时,才会对t进行排序。那么对于每一个l的块,里面r最坏情况下占据了所有的块,所以最坏情况下:有 n / b l o c k s i z e n/blocksize n/blocksize个l的块,每个l的块中会有 n / b l o c k s i z e n/blocksize n/blocksize个r的块,此时,在一个r块里,就会出现有序的t。所以t的单调段个数最多为: ( n / b l o c k s i z e ) ∗ ( n / b l o c k s i z e ) (n/blocksize)*(n/blocksize) (n/blocksize)(n/blocksize)。每个单调段最多移动t次,复杂度为 O ( ( n / b l o c k s i z e ) 2 ∗ t ) O((n/blocksize)^2*t) O((n/blocksize)2t)
故总复杂度为 O ( m ∗ b l o c k s i z e + n + n ∗ n / b l o c k s i z e + ( n / b l o c k s i z e ) 2 ∗ t ) O(m*blocksize+n+n*n/blocksize+(n/blocksize)^2*t) O(mblocksize+n+nn/blocksize+(n/blocksize)2t)
当blocksize取 ( n t ) 1 3 (nt)^\frac{1}{3} (nt)31是较优

例题

P1903 [国家集训队]数颜色 / 维护队列

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 2e5+10;
int n,m;
int a[N],cnt[10*N]/*数字的范围*/,ans[N];
int blocksize = 1;
struct query{
	int l,r,pre,id,bl,br;
}q[N];
struct change{
	int pos,pre,cur;
}cg[N];
bool cmp(query a,query b){
	if(a.bl != b.bl) return a.bl < b.bl;
	if(a.br != b.br) return a.br < b.br;
	return a.pre<b.pre;
}
int main(){
	scanf("%d%d",&n,&m);
	for(int i = 1;i<=n;i++){
		scanf("%d",&a[i]);
	}
	int totq = 0,totcg = 0;
	for(int i = 1;i<=m;i++){
		char s;
		cin >> s;
		if(s == 'Q'){
			totq++;//cin的时候++是很危险的 
			scanf("%d%d",&q[totq].l,&q[totq].r);
			q[totq].id = totq;
			q[totq].pre = totcg;
		}
		else{
			totcg++;
			scanf("%d%d",&cg[totcg].pos,&cg[totcg].cur);
			cg[totcg].pre = a[cg[totcg].pos];
			a[cg[totcg].pos] = cg[totcg].cur;
		}
	}
	blocksize=ceil(exp((log(n)+log(totcg+1/*totcg有可能是0就会变负无穷*/))/3));//统一的块的大小
	for(int i = 1;i<=totq;i++){
		q[i].bl = (q[i].l)/blocksize;
		q[i].br = (q[i].r)/blocksize;
	}
	sort(q+1,q+1+totq,cmp);
	for(int i = totcg;i>=1;i--){
		a[cg[i].pos] = cg[i].pre;
	}
	int l = 1,r = 0,num = 0,t = 0;//l=1,r=0;
	for(int i = 1;i<=totq;i++){
		int cl = q[i].l,cr = q[i].r,ct = q[i].pre;
		while(cl<l) num += !cnt[a[--l]]++;
		while(cl>l) num -= !--cnt[a[l++]];
		while(cr>r) num += !cnt[a[++r]]++;
		while(cr<r) num -= !--cnt[a[r--]];
		while(ct<t){
			if((cg[t].pos>=l) && (cg[t].pos<=r)){
				num -= !--cnt[a[cg[t].pos]];
			}
			a[cg[t].pos] = cg[t].pre;
			if((cg[t].pos>=l) && (cg[t].pos<=r)){
				num += !cnt[a[cg[t].pos]]++;
			}
			t--;
		}
		while(ct>t){
			t++;
			if((cg[t].pos>=l) && (cg[t].pos<=r)){
				num -= !--cnt[a[cg[t].pos]];
			}
			a[cg[t].pos] = cg[t].cur;
			if((cg[t].pos>=l) && (cg[t].pos<=r)){
				num += !cnt[a[cg[t].pos]]++;
			}
		}
		ans[q[i].id] = num;
	}
	for(int i = 1;i<=totq;i++) printf("%d\n",ans[i]);
	return 0;
}

zoto

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 2e5+10;
int cnt[N],sum[N],ans[N],a[N];
int blocksize;
struct query{
	int l,r,up,down,bl,br,id;
}q[N];
bool cmp(query a,query b){
	if(a.bl != b.bl) return a.bl<b.bl;
	return a.br < b.br;
}
void add(int x){
	if(++cnt[x] == 1) sum[x/blocksize]++;
}
void del(int x){
	if(--cnt[x] == 0) sum[x/blocksize]--;
}
int calc(int x){
        int now=0;
        for (int i=0;i<x/blocksize;i++) now+=sum[i];
        for (int i=(x/blocksize)*blocksize;i<=x;i++) now+=(cnt[i]>=1);
        return now;
}
int main(){
	ios::sync_with_stdio(false); cin.tie(0);
	int T,n,m;
	cin >> T;
	while(T--){
		for(int i = 0;i<=100000;i++){
			cnt[i] = 0;
			sum[i] = 0;
			a[i] = 0; 
		}
		cin >> n >> m;
		blocksize = ceil(sqrt(n));
		for(int i = 1;i<=n;i++) cin >> a[i];
		for(int i = 1;i<=m;i++){
			cin >> q[i].l >> q[i].down >> q[i].r >> q[i].up;
			q[i].id = i;
			q[i].bl = q[i].l/blocksize;
			q[i].br = q[i].r/blocksize;
		}
		sort(q+1,q+1+m,cmp);
		int l = 1,r = 0;
		for(int i = 1;i<=m;i++){
			int cl = q[i].l,cr = q[i].r;
			while(cl<l) add(a[--l]);
			while(cl>l) del(a[l++]);
			while(cr>r) add(a[++r]);
			while(cr<r) del(a[r--]);
			ans[q[i].id] = calc(q[i].up)-calc(q[i].down-1);
		}
		for(int i = 1;i<=m;i++) cout << ans[i] << endl;
	}
	return 0;
}

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值