bzoj2821: 作诗(Poetize)

本文介绍了一种基于分块(分sqrt(n)块)的算法实现思路,通过预处理将问题分解为块间和块内的查询操作,提高了查询效率。文章详细讲解了如何使用二维数组F[i][j]来表示块i到块j的答案,并使用一维数组s[i][j]记录每个数字在指定范围内的出现次数。

 分块

分sqrt(n)块

F[i][j]表示块i到块j的答案

s[i][j]表示数字i在前j块内出现了几次

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <cstdlib>
#include <algorithm>
#define N 100005
#define M 233
#define L(x) (n*((x)-1)/m+1)
#define R(x) (n*(x)/m)

using namespace std;
inline int read(){
	int ret=0;char ch=getchar();
	while (ch<'0'||ch>'9') ch=getchar();
	while ('0'<=ch&&ch<='9'){
		ret=ret*10-48+ch;
		ch=getchar();
	}
	return ret;
}


int n,a[N],l;
int bl[N],m;
int F[M][M];
int s[N][M];
int cnt[N];

void precompute(){
	m=min((int)sqrt(n),230);
	memset(cnt,0,sizeof(cnt));
	for (int i=1;i<=m;++i){
		for (int j=L(i);j<=R(i);++j){
			bl[j]=i;++cnt[a[j]];
		}
		for (int j=1;j<=l;++j) s[j][i]=cnt[j];
	}
	memset(F,0,sizeof(F));
	for (int i=1;i<=m;++i){
		memset(cnt,0,sizeof(cnt));
		int now=0;
		for (int j=i;j<=m;++j){
			for (int k=L(j);k<=R(j);++k){
				if ((++cnt[a[k]])<2) continue;
				now-=(cnt[a[k]]&1)*2-1;
			}
			F[i][j]=now;
		}
	}
	memset(cnt,0,sizeof(cnt));
}

int query(int l,int r){
	int now=F[bl[l]+1][bl[r]-1];
	if (bl[l]==bl[r]){
		for (int i=l;i<=r;++i){
			if ((++cnt[a[i]])<2) continue;
			now-=(cnt[a[i]]&1)*2-1;
		}
		for (int i=l;i<=r;++i) --cnt[a[i]];
		return now;
	}
	for (int i=l;i<=r;i=(i==R(bl[l])?L(bl[r]):(i+1))){
		if ((++cnt[a[i]])+s[a[i]][bl[r]-1]-s[a[i]][bl[l]]<2) continue;
		now-=((cnt[a[i]]+s[a[i]][bl[r]-1]-s[a[i]][bl[l]])&1)*2-1;
	}
	for (int i=l;i<=r;i=(i==R(bl[l])?L(bl[r]):(i+1))) --cnt[a[i]];
	return now;
}


int main(){
	n=read();l=read();int Q=read(),ans=0;
	for (int i=1;i<=n;++i) a[i]=read();
	precompute();
	while (Q--){
		int x=(read()+ans)%n+1,y=(read()+ans)%n+1;
		printf("%d\n",ans=query(min(x,y),max(x,y)));
	}
	
	return 0;
}

  

转载于:https://www.cnblogs.com/wangyurzee7/p/5152183.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值