[DS记录]P5072 [Ynoi2015] 盼君勿忘

command_block

2021-06-30 16:17:26

Personal

**题意** : 给出序列 $A$ ,每次查询区间 $[l,r]$ 的所有子序列分别去重后的和 $\bmod\ p$。 这里的 $p$ 每次询问单独给定,不保证是质数。 允许离线,$n,m\leq 10^5$ ,时限$\texttt{3s}$。 ------------ 分别考虑每个数的贡献。若在一个长度为 $L$ 的区间中数 $x$ 出现了 $k$ 次,则贡献为 $x(2^L-2^{L-k})$。 由于每次的 $p$ 都不同,势必要单独计算每个 $2^L-2^{L-k}$。 对于每个询问,首先用光速幂预处理,即可 $O(\sqrt{n})-O(1)$ 求解 $2^k$。 考虑根号分治。 记 $s_k$ 为出现 $k$ 次的数的总和,对于出现次数 $\leq \sqrt{n}$ 的数,计算 $\sum_{k=1}^{\sqrt{n}}s_k(2^L-2^{L-k})$。 对于出现次数 $>\sqrt{n}$ 的数,只有 $O(\sqrt{n})$ 个,暴力维护其出现次数。 使用莫队即可维护上述信息,复杂度 $O(n\sqrt{n})$。($n,m$ 同阶) 代码很好写。 ```cpp #include<algorithm> #include<cstdio> #include<cmath> #define ll long long #define MaxN 100500 using namespace std; const int lim=100000; ll s[505]; int o[MaxN],a[MaxN],cnt[MaxN],BS; inline void add(int x){ if (cnt[x]>BS){++o[x];return ;} s[o[x]]-=x;s[++o[x]]+=x; } inline void del(int x){ if (cnt[x]>BS){--o[x];return ;} s[o[x]]-=x;s[--o[x]]+=x; } struct Data{int l,r,mod,p;}b[MaxN]; bool cmp(const Data &A,const Data &B) {return A.l/BS==B.l/BS ? (A.l/BS)&1 ? A.r<B.r : A.r>B.r : A.l<B.l;} int n,m,ans[MaxN],mod ,st[505],tn,pw1[505],pw2[505]; int pow2(int n) {return 1ll*pw2[n/BS]*pw1[n%BS]%mod;} int main() { scanf("%d%d",&n,&m); BS=sqrt(n)+1; for (int i=1;i<=n;i++){ scanf("%d",&a[i]); cnt[a[i]]++; } for (int i=1;i<=lim;i++) if (cnt[i]>BS)st[++tn]=i; for (int i=1;i<=m;i++){ scanf("%d%d%d",&b[i].l,&b[i].r,&b[i].mod); b[i].p=i; } sort(b+1,b+n+1,cmp); int l=1,r=0; for (int i=1;i<=m;i++){ while(b[i].l<l)add(a[--l]); while(r<b[i].r)add(a[++r]); while(l<b[i].l)del(a[l++]); while(b[i].r<r)del(a[r--]); mod=b[i].mod; pw1[0]=pw2[0]=1; for (int i=1;i<=BS;i++)pw1[i]=(pw1[i-1]<<1)%mod; for (int i=1;i<=BS;i++)pw2[i]=1ll*pw2[i-1]*pw1[BS]%mod; int len=r-l+1,ret=0;ll ret2=0; for (int i=1;i<=tn;i++){ int x=st[i]; ret=(ret+1ll*x*pow2(len-o[x]))%mod; ret2+=x; } for (int c=1;c<=BS;c++){ ret=(ret+1ll*s[c]*pow2(len-c))%mod; ret2+=s[c]; } ans[b[i].p]=(ret2%mod*pow2(len)-ret+mod)%mod; } for (int i=1;i<=m;i++) printf("%d\n",ans[i]); return 0; } ```