Luogu P5072. [Ynoi2015] 盼君勿忘

给定一个长度为 \(n\) 的序列 \(a\),每次查询一个区间 \([l,r]\) 中所有子序列分别去重后的和 \(\bmod\ p\)
\(1\le n,q,a_i\le 10^5,1\le p\le 10^9,1\le l\le r\le n\)


考虑数 \(x\) 在区间 \([l,r]\) 中出现了 \(k\) 次的贡献。包含 \(x\) 的子序列数是 \(2^{r-l+1}-2^{r-l+1-k}\),那么贡献就是子序列数乘值。

有个显而易见的结论,每个区间中数的不同出现次数是 \(O(\sqrt{r-l+1})\) 级别的。如果维护不同的出现次数以及每种出现次数对应的数的和,那么一次询问就可以在 \(O(\sqrt{n})\) 内解决。

离线下来用莫队维护,每次加入或者删除一个数时用双向链表 \(O(1)\) 维护,再 \(O(\sqrt{n})\) 查询,所以总时间复杂度就是 \(O((n+q)\sqrt{n})\)

注意每次询问的模数不同,用快速幂会超时,所以每次询问时预处理出 \(2^{0},2^{1},\ldots,2^{\sqrt{n}-1}\)\(2^{\sqrt{n}},2^{2\sqrt{n}},\ldots,2^{n}\)\(p\) 取模的值,然后就可以通过这两个数列组合起来 \(O(1)\) 求出 \(2\) 的幂次对 \(p\) 取模的结果。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=3e5+5;
struct Node{int l,r,mod,id,bel;}q[N];
struct List{
	int nxt[N],pre[N],lst=0;
	inline void insert(int x){
		nxt[lst]=x,pre[x]=lst,lst=x;
	}
	inline void erase(int x){
		if(x^lst)nxt[pre[x]]=nxt[x],pre[nxt[x]]=pre[x];
		else nxt[pre[x]]=0,lst=pre[x];
		pre[x]=nxt[x]=0;
	}
}List;
int n,m,B,a[N],cnt[N],ans[N];ll p0[N],p1[N],sum[N];
inline bool cmp(Node &x,Node &y){
	return x.bel==y.bel?(x.bel&1?x.r<y.r:x.r>y.r):x.l<y.l;
}
inline void premod(int mod){
	p0[0]=p1[0]=1;
	for(int i=1;i<=B+5;++i)p0[i]=(p0[i-1]+p0[i-1])%mod;
	for(int i=1;i<=n/B+5;++i)p1[i]=p1[i-1]*p0[B]%mod;
}
inline ll pow2(int L,int mod){return p0[L%B]*p1[L/B]%mod;}
inline void upd(int id,int delta){
	sum[cnt[a[id]]]-=a[id];
	if(!sum[cnt[a[id]]])List.erase(cnt[a[id]]);
	cnt[a[id]]+=delta;
	if(!sum[cnt[a[id]]])List.insert(cnt[a[id]]);
	sum[cnt[a[id]]]+=a[id];
}
int main(){
	scanf("%d%d",&n,&m),B=sqrt(n);
	for(int i=1;i<=n;++i)scanf("%d",a+i);
	for(int i=1;i<=m;++i){
		scanf("%d%d%d",&q[i].l,&q[i].r,&q[i].mod);
		q[i].id=i,q[i].bel=(q[i].l-1)/B+1;
	}
	sort(q+1,q+1+m,cmp);
	int l=1,r=0;
	for(int i=1;i<=m;++i){
		premod(q[i].mod);
		while(l>q[i].l)upd(--l,1);
		while(r<q[i].r)upd(++r,1);
		while(l<q[i].l)upd(l++,-1);
		while(r>q[i].r)upd(r--,-1);
		for(int x=List.nxt[0];x;x=List.nxt[x])
			(ans[q[i].id]+=(pow2(r-l+1,q[i].mod)-pow2(r-l+1-x,q[i].mod)+q[i].mod)%q[i].mod*(sum[x]%q[i].mod)%q[i].mod)%=q[i].mod;
	}
	for(int i=1;i<=m;++i)printf("%d\n",ans[i]);
	return 0;
}
posted @ 2022-07-28 16:25  Samsara-soul  阅读(36)  评论(0编辑  收藏  举报