【洛谷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;
}
posted @ 2020-09-23 10:16  stoorz  阅读(138)  评论(0编辑  收藏  举报