【洛谷P5072】盼君勿忘
题目
题目链接:https://www.luogu.com.cn/problem/P5072
珂朵莉给了你一个序列,每次查询一个区间 \([l,r]\) 中所有子序列分别去重后的和 \(\bmod\ p\)。
思路
第一道 Ynoi 黑 /fad。
考虑每一个数在区间 \([l,r]\) 中的贡献。假设这个数字出现了 \(k\) 次,那么贡献为 \(2^{r-l+1}-2^{r-l+1-k}\)。
那么记 \(cnt[i]\) 表示数字 \(i\) 出现的次数,记 \(sum[i]\) 表示出现次数为 \(i\) 的数的和,那么每次询问答案即为
\[\sum^{n}_{i=1}sum[i]\times (2^{r-l+1}-2^{r-l+1-i})
\]
莫队乱搞即可。时间复杂度 \(O(nm+m\sqrt{n})\)。显然不够优秀。
发现出现次数超过 \(\sqrt{n}\) 的数字不会超过 \(\sqrt{n}\) 个,所以可以用一个 \(\operatorname{unordered\_set}\) 记录出现次数超过 \(\sqrt{n}\) 的数字。
然后每次询问枚举出现次数时只需要枚举到 \(\sqrt{n}\)。剩余不超过 \(\sqrt{n}\) 个数字直接算即可。
但是每一次询问的模数不一样,如果对于每一个 \(2^i\) 都计算一次时间复杂度会乘上一个 \(\log n\)。所以我们可以每次询问与处理出 \(2^{i}(0\leq i<T)\) 和 \(2^j(T|j)\)。然后每一个 \(2^k\) 都可以 \(O(1)\) 表示出来了。
时间复杂度 \(O(m\sqrt{n})\)。
代码
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=100010,T=320;
int n,Q,a[N],cnt[N];
ll ans[N],power[2][T],sum[T];
unordered_set<ll> s;
struct Query
{
int l,r,p,id,bel;
friend bool operator <(Query x,Query y)
{
return (x.bel==y.bel)?(x.r<y.r):(x.bel<y.bel);
}
}ask[N];
int read()
{
int d=0; char ch=getchar();
while (!isdigit(ch)) ch=getchar();
while (isdigit(ch)) d=(d<<3)+(d<<1)+ch-48,ch=getchar();
return d;
}
void ins(int x)
{
if (cnt[x]<T) sum[cnt[x]]-=x;
cnt[x]++;
if (cnt[x]==T) s.insert(x);
if (cnt[x]<T) sum[cnt[x]]+=x;
}
void del(int x)
{
if (cnt[x]<T) sum[cnt[x]]-=x;
if (cnt[x]==T) s.erase(s.find(x));
cnt[x]--;
if (cnt[x]<T) sum[cnt[x]]+=x;
}
int main()
{
n=read(); Q=read();
for (int i=1;i<=n;i++)
a[i]=read();
int WYCtxdy=sqrt(n);
for (int i=1;i<=Q;i++)
{
ask[i]=(Query){read(),read(),read(),i,114514};
ask[i].bel=ask[i].l/WYCtxdy;
}
sort(ask+1,ask+1+Q);
for (int i=1,l=1,r=0;i<=Q;i++)
{
ll MOD=ask[i].p;
for (;l>ask[i].l;l--) ins(a[l-1]);
for (;r<ask[i].r;r++) ins(a[r+1]);
for (;l<ask[i].l;l++) del(a[l]);
for (;r>ask[i].r;r--) del(a[r]);
power[0][0]=power[1][0]=1%MOD;
for (int j=1;j<T;j++) power[0][j]=power[0][j-1]*2LL%MOD;
power[1][1]=power[0][T-1]*2LL%MOD;
for (int j=2;j<T;j++) power[1][j]=power[1][j-1]*power[1][1]%MOD;
int p1=(r-l+1)/T,p2=(r-l+1)%T,id=ask[i].id;
for (int j=1;j<T;j++)
{
int q1=(r-l+1-j)/T,q2=(r-l+1-j)%T;
ans[id]=(ans[id]+sum[j]%MOD*((power[1][p1]*power[0][p2]-power[1][q1]*power[0][q2])%MOD))%MOD;
}
for (unordered_set<ll>::iterator it=s.begin();it!=s.end();it++)
{
int q1=(r-l+1-cnt[*it])/T,q2=(r-l+1-cnt[*it])%T;
ans[id]=(ans[id]+*it*((power[1][p1]*power[0][p2]-power[1][q1]*power[0][q2])%MOD))%MOD;
}
ans[id]=(ans[id]+MOD)%MOD;
}
for (int i=1;i<=Q;i++)
printf("%lld\n",ans[i]);
return 0;
}