Yoi #321. 永不加班

题面

现在你得到了一个任务,给你一个长度为n的数列,数列上的每个点都有一个颜色。
你会得到q次询问,每次问你一段区间里有多少个子序列满足子序列的所有点的颜色互不相同(子序列可以为空),答案对998244353取模。
永不加班的你希望可以做到实时回答询问,所以询问强制在线,即每次的询问要异或上次的答案。

分析

经典分块

这每种颜色有\(c_{i}\)种,则答案显然为\(\prod_{i}c_{i}\)

预处理出块与块之间的答案,块与块之间的各种颜色数量(对块进行颜色前缀和)

枚举不在整块里的\(2\sqrt{n}\)个点,暴力删去再加入即可,期望时间\(O(nlgn)\)

#include<bits/stdc++.h>
#define ll long long
const int p=998244353;
using namespace std;

const int N=1e5+5,M=405;
int inv[N],n,a[N],bel[N],m,f[M][M],s[M][N],t[N];
ll ans;

int main() {
	freopen("color.in","r",stdin);
	freopen("color.out","w",stdout);
	inv[1]=1;
	for(int i=2;i<=100001;i++) {
		inv[i]=(ll)(p-p/i)*inv[p%i]%p;
	}	
	scanf("%d",&n);
	for(int i=1;i<=n;i++) {
		scanf("%d",&a[i]),m=max(m,a[i]);
	}
	int S=sqrt(n);
	for(int i=1;i<=n;i++) bel[i]=(i-1)/S+1;
	for(int j=1;j<=bel[n];j++) {
		ans=1;
		for(int i=j;i>=1;i--) {
			for(int k=(i-1)*S+1;k<=min(n,i*S);k++) {
				t[a[k]]++;
				ans=ans*inv[t[a[k]]]%p*(t[a[k]]+1)%p;
			}
			f[i][j]=ans;
		}
		for(int i=1;i<=m;i++) {
			s[j][i]=t[i],t[i]=0;
		}
	}
	int T; scanf("%d",&T); ans=0;
	while(T--) {
		int l,r; scanf("%d%d",&l,&r),l^=ans,r^=ans;
		int t1=bel[l],t2=bel[r];
		if(t2-t1<=1) {
			ans=1;
			for(int i=l;i<=r;i++) t[a[i]]++;	
			for(int i=l;i<=r;i++) {
				ans=ans*(t[a[i]]+1)%p;
				t[a[i]]=0;
			}
			printf("%lld\n",ans);
		}else {
			ans=f[t1+1][t2-1];
			for(int i=l;i<=t1*S;i++) {
				int num=s[t2-1][a[i]]-s[t1][a[i]]+t[a[i]];
				ans=ans*inv[num+1]%p*(num+2)%p;
				t[a[i]]++;
			}
			for(int i=(t2-1)*S+1;i<=r;i++) {
				int num=s[t2-1][a[i]]-s[t1][a[i]]+t[a[i]];
				ans=ans*inv[num+1]%p*(num+2)%p;
				t[a[i]]++;
			}
			for(int i=l;i<=t1*S;i++) t[a[i]]=0;
			for(int i=(t2-1)*S+1;i<=r;i++) t[a[i]]=0;
			printf("%lld\n",ans);
		}
	}
	return 0;
}

posted @ 2020-10-25 19:06  wwwsfff  阅读(188)  评论(0)    收藏  举报