『各种分块』lorem

题目描述

当然,人不能总是回忆过去,更要向前看。

Magolor准备为9102的选手们出一道题。当然是一道简单题,虽然2019年的选手可能不会这样认为。因为9102年与2019年相比变化很大,甚至连语言都相差甚远,Lorem Ipsum 在9102年已经成为世界唯一通用语言。显然,题目的题面是使用Lorem Ipsum 写的。

Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmodtempor incididunt ut labore et dolore magna aliqua. Ut enim ad minimveniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex eacommodo consequat. Duis aute irure dolor in reprehenderit in volupta tevelit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id
est laborum.
这份题面不仅有文采,而且颇具迷惑性。作为一名要参加NOIP9102的选手,相信你可以一眼看出解决这道题的关键就是对于q个关于这个长度为n的序列A的询问(l,r,MOD) ,分别求出区间[l,r]内所有 dolor 的 ut 值的和对 MOD 取余的结果。

对于100%的数据, 1 &lt; = n , q , A i &lt; = 1 0 5 , 1 &lt; = M O D &lt; = 1 0 9 1&lt;=n,q,Ai&lt;=10^5,1&lt;=MOD&lt;=10^9 1<=n,q,Ai<=105,1<=MOD<=109

题解

我们思考某一个数 x x x对答案产生的贡献,那么一定可以表示为:不含x的子序列个数 × \times ×含x的子序列个数

如何理解呢?显然某一个要在子序列中做出贡献,必须要满足存在这个数;而且不管有多少个这个数只能够计算一次。我们便吧单独的一个只含有x的子序列单独的拎出来,与其它的任意不含x的子序列组合即可。因此每一个数字 x x x的贡献可以表示为(假设出现次数为 c , l e n = r − l + 1 c,len=r-l+1 clen=rl+1): x × ( 2 l e n − c × ( 2 c − 1 ) ) = x × ( 2 l e n − 2 l e n − c ) x\times(2^{len-c}\times (2^c-1))=x\times (2^{len}-2^{len-c}) x×(2lenc×(2c1))=x×(2len2lenc)

分块 1 1 1:莫队·询问分块

因此我们如果在模数一样的情况下,我们应该如何解决呢?对于区间询问问题我们考虑莫队算法。
但是如果算式的模数不一样呢?

我们来考虑一下出现次数,统计每一个对应的出现次数的权值和,最后用这个权值和乘上 2 l e n − 2 l e n − c 2^{len}-2^{len-c} 2len2lenc即可。那么如何快速的统计出现次数呢?因为如果每一次我们都枚举出现次数,时间仍然是平方级别的。

分块 2 2 2:按照数值的出现次数分块
  • 如果某一个数出现次数是大于 n \sqrt n n 的,我们可以将其单独处理而不再莫队算法中考虑。在统计答案的时候枚举每一个数,用二维前缀和即可得到对应的出现次数。时间复杂度: O ( n n ) O(n\sqrt n) O(nn ).
  • 如果某一个数的出现次数是小于 n \sqrt n n 的,我们可以对对应次数新累加上一个值,然后把原来次数上的数值删去。那么我们就可以得到对应次数的权值和了。最后做的时候直接枚举次数即可。时间复杂度: O ( n n ) O(n\sqrt n) O(nn ).
分块 3 3 3:对指数分块

我们发现要求出不同模数的 2 k 2^k 2k,如果使用快速幂时间复杂度带有log会超市,我们可以按照指数分块。

即, k = a × n + b k=a\times \sqrt n+b k=a×n +b,我们可以预处理 2 0 , 2 1 , 2 2 , 2 3 , ⋯ 2^0,2^1,2^2,2^3,\cdots 20,21,22,23,,同时处理 2 1 , 2 2 , 2 3 , ⋯ 2^{\sqrt 1},2^{\sqrt 2},2^{\sqrt 3},\cdots 21 ,22 ,23 然后就可以十分方便的在 O ( n ) O(\sqrt n) O(n )内完成预处理, O ( 1 ) O(1) O(1)查询。

代码如下:

#include <bits/stdc++.h>
#define ll long long
#define dec(a,b) ((a-b)%P+P)%P

using namespace std;
const int T = 350;
const int N = 200000;

int n, m, Max = 0, tot = 0, P;
int vis[N], Num[N], cnt1[500][N], a[N];
int power1[N], power2[N], cnt[N], cnt2[N], Ans[N];
ll sum[N];
struct node {
	int l, r, mod, bel, id;
}q[N];

int read(void)
{
	int s = 0, w = 0; char c = getchar();
	while (c < '0' || c > '9') w |= c == '-', c = getchar();
	while (c >= '0' && c <= '9') s = s*10+c-48, c = getchar();
	return w ? -s : s;
}

bool cmp(node p1,node p2) {
	return p1.bel == p2.bel ? p1.r < p2.r : p1.bel < p2.bel;
}

void init(void)
{
	for (int i=1;i<=1e5;++i)
	    if (cnt[i] > T) Num[++tot] = i, vis[i] = 1;
	for (int i=1;i<=tot;++i)
	    for (int j=1;j<=n;++j)
	        cnt1[i][j] = cnt1[i][j-1] + (Num[i] == a[j]);
	return;
}

void get_power(void)
{
    power1[0] = power2[0] = 1;
    for (int i=1;i<=T;++i) power1[i] = 1LL * power1[i-1] * 2 % P;
    for (int i=1;i<=T;++i) power2[i] = 1LL * power2[i-1] * power1[T] % P;
    return;
} 

int power(int x) {
	return 1LL * power1[x % T] * power2[x / T] % P;
}

void ins(int x)
{
	if (vis[x]) return ;
	sum[cnt2[x]] -= x;
	sum[++cnt2[x]] += x;
}

void del(int x)
{
	if (vis[x]) return;
	sum[cnt2[x]] -= x;
	sum[--cnt2[x]] += x;
} 

int main(void)
{
	freopen("lorem.in","r",stdin);
	freopen("lorem.out","w",stdout);
	n = read(), m = read();
	for (int i=1;i<=n;++i)
	{
		a[i] = read();
		cnt[a[i]] ++; 
	}
	init();
	for (int i=1;i<=m;++i)
	{
		q[i].l = read(), q[i].r = read(), q[i].mod = read();
		q[i].id = i, q[i].bel = (q[i].l-1) / T;
	}
	sort(q+1,q+m+1,cmp);
	int l = 1, r = 0;
	for (int i=1;i<=m;++i)
	{
		while (l > q[i].l) ins(a[--l]);
		while (r < q[i].r) ins(a[++r]);
		while (l < q[i].l) del(a[l++]);
		while (r > q[i].r) del(a[r--]);
		P = q[i].mod;
		get_power();
		int ans = 0, len = r-l+1;
		for (int j=1;j<=T;++j)
		    ans = (ans + 1LL * sum[j] % P * dec(power(len),power(len-j)) % P) % P;
		for (int j=1;j<=tot;++j)
		    ans = (ans + 1LL * Num[j] % P * dec(power(len),power(len-cnt1[j][r]+cnt1[j][l-1])) %P) % P;
		Ans[q[i].id] = ans;
	}
	for (int i=1;i<=m;++i) printf("%d\n", Ans[i]);
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值