LibreOJ 6499. 「雅礼集训 2018 Day2」颜色【分块+bitset+ST表+卡常】

6499. 「雅礼集训 2018 Day2」颜色

【题目描述】

传送门

【题解】

分块+bitset+ST表+卡常

对于序列分块,每块用bitset存起来,然后用ST表预处理bitset的合并,然后就是一道卡常题了。

代码如下

#include<cmath>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int MAXN=100005,MAXQ=55,MAXP=10000/3+1,MOD=(1<<16)-1;
int n,m,P,K,Siz,RSiz,Ans,a[MAXN],block[MAXN],hsh[MAXN],Num[MOD+5],LOG[MAXQ];
#include<cctype>
char nc(){
	static char buf[100000],*L=buf,*R=buf;
	return (L==R&&(R=(L=buf)+fread(buf,1,100000,stdin),L==R))?EOF:*L++;
}
int read(){
	int ret=0;char ch=nc();bool f=1;
	for(;!isdigit(ch);ch=nc()) f^=!(ch^'-');
	for(; isdigit(ch);ch=nc()) ret=ret*10+ch-48;
	return f?ret:-ret;
}
struct BITSET{
	unsigned int a[MAXP+5];
	BITSET operator |(const BITSET b)const{
		BITSET c;
		for(int i=0;i<=K;++i) c.a[i]=a[i]|b.a[i];
		return c;
	}
	void set(int x){a[x>>5]|=1u<<(x&31);}
	void reset(){for(int i=0;i<=K;++i) a[i]=0;}
	int count(){
		int Now=0;
		for(int i=0;i<=K;++i) Now+=Num[a[i]&MOD],Now+=Num[a[i]>>16];
		return Now;
	}
}f[MAXQ][7],s;
int main(){
	n=read(),m=read(),P=read();
	for(int i=0;i<65536;i++){
		Num[i]=0;
		for(int x=i;x;x-=x&-x) ++Num[i];
	} 
	for(int i=2;i<MAXQ;i++) LOG[i]=LOG[i>>1]+1;
	for(int i=1;i<=n;++i) hsh[i]=a[i]=read();
	sort(hsh+1,hsh+1+n);K=unique(hsh+1,hsh+1+n)-hsh-1;
	for(int i=1;i<=n;++i) a[i]=lower_bound(hsh+1,hsh+1+K,a[i])-hsh-1;
	Siz=2000;K>>=5;
	for(int i=1;i<=n;++i){
		block[i]=block[i-1];
		if(i%Siz==0) ++block[i],++RSiz;
		f[block[i]][0].set(a[i]);
	}
	for(int j=1;j<=LOG[RSiz];++j)
	for(int i=1;i<=RSiz-(1<<j)+1;++i) f[i][j]=f[i][j-1]|f[i+(1<<j-1)][j-1];
	for(int j=0;j<m;++j){
		int K=read();s.reset();
		while(K--){
			int L=read(),R=read();
			if(P&&j) L=(L^Ans)%n+1,R=(R^Ans)%n+1; 
			if(L>R) swap(L,R);
			if(block[L]==block[R]) for(int i=L;i<=R;++i) s.set(a[i]);
			else{
				if(block[L]==block[L-1]){
					int i;
					for(i=L;block[i]==block[L];++i) s.set(a[i]);
					L=i;
				}
				if(block[R]==block[R+1]){
					int i;
					for(i=R;block[i]==block[R];--i) s.set(a[i]);
					R=i;
				}
				if(block[L]<=block[R]){
					int k=LOG[block[R]-block[L]+1];
					s=s|f[block[L]][k]|f[block[R]-(1<<k)+1][k];
				}
			}
		}
		printf("%d\n",Ans=s.count());
	}
	return 0;
}

转载于:https://www.cnblogs.com/XSamsara/p/10547936.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值