LGP5072 [Ynoi 2015-E] 盼君勿忘 学习笔记
LGP5072 [Ynoi 2015-E] 盼君勿忘 学习笔记
题意简述
给定一个长为 \(n\) 的序列。\(m\) 次询问,每次查询一个区间 \([l_i,r_i]\) 中所有子序列分别去重后的和 \(\bmod\,p_i\)。
做法解析
首先先拆开这坨“所有子序列分别去重后的和”,我们发现,对于一次询问,设 \(len=r-l+1\),数字 \(x\) 在其中出现 \(t\) 次,那么此数字做出的贡献就是 \(2^{len}-2^{len-t}\)(总共有 \(2^len\) 个子序列(包括空序列),要想一个 \(t\) 不选就要从剩余的 \(len-t\) 个元素中自由组合)。
然后我们就可以莫队了?我们维护每种元素当前区间内的出现次数与出现 \(t\) 次的元素的和 \(sum_t\)。这样的话,更新答案的时候求 \(\sum_{t=1}^{n} sum_t\times 2^{len}-2^{len-t}\) 就行了。当然,我们不能真的把 \(t\) 枚举到 \(n\),而要根据出现次数根号分治,对于 \(t>\sqrt{n}\) 的元素我们开个map单独维护。
最后你发现这个 \(2\) 的幂次由于每次询问模数不一样所以都要重新处理一次,但是每处理一次如果按普通的方法是 \(O(n)\) 的,所以我们要用光速幂,也就是预处理出 \(2^1,2^2,\dots,2^{B-1}\) 以及 \(2^B,2^{2B},\dots,2^n\) 的值,然后需要求幂次值时 \(O(1)\) 拼接,一般地取 \(B=\sqrt{n}\)。
好了这题就做完了。
代码实现
#include <bits/stdc++.h>
using namespace std;
using namespace obasic;
const int MaxN=1e5+5,MaxNr=325;
int N,M,Nr,A[MaxN],bel[MaxN],apr[MaxN],cnt[MaxN];
struct quer{int l,r,p,id;}Q[MaxN];
bool cmp(quer a,quer b){return bel[a.l]==bel[b.l]?(bel[a.l]&1?a.r<b.r:a.r>b.r):a.l<b.l;}
lolo cpw2[2][MaxNr],ans[MaxN],curp;
void initpow(int p){
cpw2[0][0]=cpw2[1][0]=1,curp=p;
for(int i=1;i<=Nr;i++)cpw2[0][i]=cpw2[0][i-1]*2%p;
for(int i=1;i<=N/Nr;i++)cpw2[1][i]=cpw2[1][i-1]*cpw2[0][Nr]%p;
}
lolo cpow(int x){return cpw2[0][x%Nr]*cpw2[1][x/Nr]%curp;}
unordered_map<int,int> ump;
void mocadd(int id){
int &cc=cnt[A[id]];
if(cc>Nr){ump[A[id]]++,cc++;return;}
apr[cc]-=A[id],cc++;(cc<=Nr)?apr[cc]+=A[id]:ump[A[id]]=cc;
}
void mocdel(int id){
int &cc=cnt[A[id]];
if(cc<=Nr){apr[cc]-=A[id],cc--,apr[cc]+=A[id];return;}
ump[A[id]]--,cc--;if(cc==Nr)ump.erase(A[id]),apr[cc]+=A[id];
}
int main(){
readis(N,M),Nr=sqrt(N);
for(int i=1;i<=N;i++)readi(A[i]),bel[i]=i/Nr+1;
for(int i=1;i<=M;i++)readis(Q[i].l,Q[i].r,Q[i].p),Q[i].id=i;
sort(Q+1,Q+M+1,cmp);
for(int i=1,cl=1,cr=0,clen,cp;i<=M;i++){
cp=Q[i].p,initpow(Q[i].p);
while(cl>Q[i].l)mocadd(--cl);
while(cr<Q[i].r)mocadd(++cr);
while(cl<Q[i].l)mocdel(cl++);
while(cr>Q[i].r)mocdel(cr--);
lolo &cans=ans[Q[i].id];clen=cr-cl+1;
for(int j=1;j<=min(Nr,clen);j++)(cans+=apr[j]*(cp+cpow(clen)-cpow(clen-j)))%=cp;
for(auto [k,val] : ump)(cans+=k*(cp+cpow(clen)-cpow(clen-val)))%=cp;
}
for(int i=1;i<=M;i++)writil(ans[i]);
return 0;
}
浙公网安备 33010602011771号