P5072 [Ynoi2015]盼君勿忘

题目链接

题意分析

插点题外话

我感觉我已经不可能再获得幸福了
因为
我已经被幸福包围了

珂朵莉,欢迎回家

每一次做这种题面都会泪目的

由于这种题只存在询问 所以我们不自觉的想到了莫队

首先 如果区间[l,r]中的一个数x出现了k次的话 那么ta在所有子序列里面出的次数就是(2^{r-l+1}-2^{r-l+1-k})

然后的话 我们使用莫队 维护每一个数x出现了的次数k 出现k次的数的和 区间里面所有的k

每一个数x出现了的次数k:由于(a_i≤10^5) 所以我们直接开桶维护就可以了 离散化都不带的

出现k次的数的和:我们也可以直接使用数组维护 但是需要注意的是为了防止重复 我们需要在每一次修改次数的时候进行特判

区间里面所有的k:这里的话也是需要去重的 我们可以使用一个叫做unordered_set的东西加以维护 就像set一样具有insert,erase,find等操作

跟set相比 unordered_set正如其名 不存在有序性 这里也不需要什么有序性 所以可以当作一个可去重的数组来使用

然后的话 我们直接通过unorder_set里面的k进行答案的累加就可以了

等一下 你觉得这样的话 就可以过了吗?

这题是需要卡常数的看在珂朵莉的面子上我就不追究了

卡常数技巧:

1.莫队排序的奇偶优化

struct Node
{
	int le,ri,id;
	long long mod;
	friend bool operator <(const Node &A,const Node &B)
	{
		if(bel[A.le]^bel[B.le]) return A.le<B.le;
		else return (bel[A.le]&1) ? A.ri<B.ri : A.ri>B.ri;
	}
}e[N];

2.求2的指数幂时的优化

由于这里指数的最高上限是n 所以我们可以考虑一下(sqrt{n})预处理求指数幂

就是(2^x=2^{frac{x}{sqrt{n}}×sqrt{n}+x\%sqrt{n}})

//sq=sqrt(n);

long long getpow(int x,long long p)
{return (qpow[(x/sq)*sq]*qpow[x%sq])%p;}

for(int j=1;j<=sq;++j) qpow[j]=qpow[j-1]*2LL%e[i].mod;
for(int j=2;j*sq<=(e[i].ri-e[i].le+1);++j)

3.undered_set的使用

【安逸一个不戳的大佬博客】

为什么我们使用unordered_set而不使用set

由于跟set相比 unordered_set更像是一个散列表 所以insert、erase以及find的平均复杂度相比于set来讲优秀很多

自然也就成为了一个卡常数技巧

CODE:

#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<cstdlib>
#include<cmath>
#include<unordered_set>
#define N 100080
using namespace std;
int n,q,tot,sq;
int num[N],vis[N],bel[N];
long long qpow[N],sum[N],ans[N];
unordered_set<int> allsum;
struct Node
{
	int le,ri,id;
	long long mod;
	friend bool operator <(const Node &A,const Node &B)
	{
		if(bel[A.le]^bel[B.le]) return A.le<B.le;
		else return (bel[A.le]&1) ? A.ri<B.ri : A.ri>B.ri;
	}
}e[N];
long long getpow(int x,long long p)
{return (qpow[(x/sq)*sq]*qpow[x%sq])%p;}
void add(int x)
{
	if(vis[num[x]])
	{
		sum[vis[num[x]]]-=num[x];
		if(!sum[vis[num[x]]]) allsum.erase(vis[num[x]]);
	}
	vis[num[x]]++;
	if(!sum[vis[num[x]]])
	{
		sum[vis[num[x]]]+=num[x];
		allsum.insert(vis[num[x]]);
	}
	else sum[vis[num[x]]]+=num[x];
}
void del(int x)
{
	sum[vis[num[x]]]-=num[x];
	if(!sum[vis[num[x]]]) allsum.erase(vis[num[x]]);
	vis[num[x]]--;
	if(!vis[num[x]]) return;
	
	if(!sum[vis[num[x]]])
	{
		sum[vis[num[x]]]+=num[x];
		allsum.insert(vis[num[x]]);
	}
	else sum[vis[num[x]]]+=num[x];
}
void solve()
{
	int lenow=1,rinow=0;
	for(int i=1;i<=q;++i)
	{
		qpow[0]=1LL;
		for(int j=1;j<=sq;++j) qpow[j]=qpow[j-1]*2LL%e[i].mod;
		for(int j=2;j*sq<=(e[i].ri-e[i].le+1);++j)
		qpow[j*sq]=qpow[(j-1)*sq]*qpow[sq]%e[i].mod;
		while(rinow<e[i].ri) add(++rinow);
		while(lenow>e[i].le) add(--lenow);
		while(lenow<e[i].le) del(lenow++);
		while(rinow>e[i].ri) del(rinow--);
		for(unordered_set<int>::iterator key=allsum.begin();key!=allsum.end();++key)
		{
			int now=*key;
			ans[e[i].id]=(ans[e[i].id]+sum[now]*(getpow(rinow-lenow+1,e[i].mod)-getpow(rinow-lenow+1-now,e[i].mod)+e[i].mod)%e[i].mod)%e[i].mod;
		}
	}
}
int main()
{
	scanf("%d%d",&n,&q);sq=(int)sqrt(1.0*n);
	for(int i=1;i<=n;++i) scanf("%d",&num[i]);
	for(int i=1;i<=n;++i) bel[i]=(i/sq)+1;
	
	for(int i=1;i<=q;++i)
	{
		scanf("%d%d%lld",&e[i].le,&e[i].ri,&e[i].mod);
		e[i].id=i;
	}
	sort(e+1,e+q+1);
	solve();
	for(int i=1;i<=n;++i)
	printf("%lld
",ans[i]);
	return 0;
} 
原文地址:https://www.cnblogs.com/LovToLZX/p/14028122.html