[BZOJ2506]calc
[BZOJ2506]calc
试题描述
给一个长度为n的非负整数序列A1,A2,…,An。现有m个询问,每次询问给出l,r,p,k,问满足l<=i<=r且Ai mod p = k的值i的个数。
输入
第一行两个正整数n和m。
第二行n个数,表示A1,A2,…,An。
以下m行,每行四个数分别表示l,r,p,k。满足1<=l<=r<=n。
输出
对于每个询问,输出一行,表示可行值i的个数。
输入示例
5 2 1 5 2 3 7 1 3 2 1 2 5 3 0
输出示例
2 1
数据规模及约定
0<n,m<=10^5,任意1<=i<=n满足Ai<=10^4,0<p<=10^4,0<=k<p。
题解
首先这个问题满足区间减法,所以可以离线搞,把一个询问拆成两个询问(即“右端点”减去“左端点-1”)。然后每个询问变成这个样子:在一个前缀中满足 Ai mod p = k 的 i 有多少个。
因为是每个前缀,所以我们可以按照右端点从左到右排序,然后每次右端点往右移一个单位就是往集合中加入一个数,不带删除。那么我们可以对 p 的大小分类讨论,令 B = max{Ai},若 p > sqrt(B) 那么直接暴力找 {k, k + p, k + 2p, k + 3p, ...} 这些数都有多少个并累加;若 p ≤ sqrt(B) 那么因为 p 和 k 都小于等于 sqrt(B),我们就可以对于所有的数对 (p, k) 维护 Ai mod p = k 的 i 有多少个。
#include <iostream> #include <cstdio> #include <cstdlib> #include <cstring> #include <cctype> #include <algorithm> #include <cmath> using namespace std; int read() { int x = 0, f = 1; char c = getchar(); while(!isdigit(c)){ if(c == '-') f = -1; c = getchar(); } while(isdigit(c)){ x = x * 10 + c - '0'; c = getchar(); } return x * f; } #define maxn 100010 #define maxa 10010 #define maxsqr 110 int n, A[maxn], sma[maxsqr][maxsqr], lar[maxa], ans[maxn]; struct Que { int x, p, k, id, tp; Que() {} Que(int _1, int _2, int _3, int _4, int _5): x(_1), p(_2), k(_3), id(_4), tp(_5) {} bool operator < (const Que& t) const { return x < t.x; } } qs[maxn<<1]; int main() { n = read(); int q = read(), cq = 0, m = 0; for(int i = 1; i <= n; i++) A[i] = read(), m = max(m, A[i]); for(int i = 1; i <= q; i++) { int l = read(), r = read(), p = read(), k = read(); qs[++cq] = Que(l - 1, p, k, i, -1); qs[++cq] = Que(r, p, k, i, 1); } sort(qs + 1, qs + cq + 1); m = sqrt(m); for(int i = 1, j = 1; i <= cq; i++) { while(j <= n && j <= qs[i].x) { for(int k = 1; k <= m; k++) sma[k][A[j]%k]++; lar[A[j]]++; j++; } if(qs[i].p <= m) ans[qs[i].id] += qs[i].tp * sma[qs[i].p][qs[i].k]; else { int sum = 0; for(int val = qs[i].k; val < maxa; val += qs[i].p) sum += lar[val]; ans[qs[i].id] += qs[i].tp * sum; } } for(int i = 1; i <= q; i++) printf("%d\n", ans[i]); return 0; }