bzoj 2506 calc 离线处理+权值分块

题面

题目传送门

解法

叫权值分块感觉挺诡异的……

  • 我们不妨离线处理询问,对于一个询问可以变成求解 [ 1 , r ] [1,r] [1,r]中满足条件的个数- [ 1 , l − 1 ] [1,l-1] [1,l1]中满足条件的个数。
  • 由于权值到 1 0 4 10^4 104,无法直接暴力实现。那么我们考虑将这些数分成2部分来实现。对于不超过 100 100 100的模数 p p p,记 f [ i ] [ j ] f[i][j] f[i][j]表示当前做到的数中,对 i i i取模为 j j j的数的个数。对于不超过 100 100 100的询问可以直接通过 f f f数组来查询。对于超过 100 100 100的模数 p p p,记 g [ i ] g[i] g[i]表示当前做到的数中, i i i这个数出现的次数。那么当前询问的答案即为 ∑ i = 0 g [ i p + k ] \sum_{i=0} g[ip+k] i=0g[ip+k]
  • 可以发现,对于上述两种情况,我们单次处理的时间复杂度均为 O ( a i ) O(\sqrt a_i) O(a i)
  • 时间复杂度: O ( m a i ) O(m\sqrt a_i) O(ma i)

代码

#include <bits/stdc++.h>
#define N 200010
using namespace std;
template <typename T> void chkmax(T &x, T y) {x = x > y ? x : y;}
template <typename T> void chkmin(T &x, T y) {x = x > y ? y : x;}
template <typename T> void read(T &x) {
	x = 0; int f = 1; char c = getchar();
	while (!isdigit(c)) {if (c == '-') f = -1; c = getchar();}
	while (isdigit(c)) x = x * 10 + c - '0', c = getchar(); x *= f;
}
struct Node {
	int x, p, k, fl, id;
	bool operator < (const Node &b) const {return x < b.x;}
} b[N];
int a[N], sum[N], ans[N][2], s[110][110];
void add(int x) {
	for (int i = 1; i <= 100; i++) s[i][a[x] % i]++;
	sum[a[x]]++;
}
int main() {
	int n, m, tot = 0, mx = 0; read(n), read(m);
	for (int i = 1; i <= n; i++)
		read(a[i]), chkmax(mx, a[i]);
	for (int i = 1; i <= m; i++) {
		int l, r, p, k;
		read(l), read(r), read(p), read(k);
		b[++tot] = (Node) {l - 1, p, k, 0, i};
		b[++tot] = (Node) {r, p, k, 1, i};
	}
	sort(b + 1, b + tot + 1); int now = 1;
	for (int i = 1; i <= tot; i++) {
		while (now <= b[i].x) add(now++);
		if (b[i].p > 100) {
			for (int j = b[i].k; j <= mx; j += b[i].p)
				ans[b[i].id][b[i].fl] += sum[j];
		} else ans[b[i].id][b[i].fl] = s[b[i].p][b[i].k];
	}
	for (int i = 1; i <= m; i++) cout << ans[i][1] - ans[i][0] << "\n";
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值