1. 程式人生 > 其它 >【分類討論】JZOJ_4744 同餘

【分類討論】JZOJ_4744 同餘

分類討論地優化樸素演算法

題面



對於每個詢問即算出區間有多少個數\(\%p=q\)

思路

考慮將問題拆開來並排序,這樣每個問題就變成了詢問\(1\sim r\)中有多少個\(kp+q\)
維護⼀個雜湊陣列,\(h_i\)表示\(i\)有多少個;
以及⼀個模數陣列\(g_{i,j}\),表示模\(i\)\(j\)有多少個。
分別在兩個陣列內標記,複雜度\(O(nk)\)

因為\(p\)越小\(k\)就越大,考慮較大時直接用桶來統計,較小時直接列舉。
每次詢問時,對於較⼩的\(p\)直接在\(g\)中查詢,對於較⼤的\(p\)列舉\(k\)並在\(h\)中查詢,複雜度\(O(m\frac{a_i}{k})\)
根據基本不等式,當\(k=\sqrt(a_i)=100\)

時最優。

程式碼

#include <cstdio>
#include <algorithm>

int n, m;
struct node {
	int r, p, q, ans, id;
} que[200001];
int a[100001], h[100001], g[101][100];

int cmp1(node x, node y) {
	return x.r < y.r;
}

int cmp2(node x, node y) {
	return x.id == y.id ? x.r < y.r : x.id < y.id;
}

int main() {
	scanf("%d %d", &n, &m);
	for (int i = 1; i <= n; i++)
		scanf("%d", &a[i]);
	for (int i = 1; i <= m; i++) {
		scanf("%d %d %d %d", &que[i * 2 - 1].r, &que[i * 2].r, &que[i * 2 - 1].p, &que[i * 2 - 1].q);
		que[i * 2 - 1].r--;
		que[i * 2].p = que[i * 2 - 1].p;
		que[i * 2].q = que[i * 2 - 1].q;
		que[i * 2 - 1].id = que[i * 2].id = i;
	}
	std::sort(que + 1, que + 2 * m + 1, cmp1);
	int pt = 1;
	for (int i = 1; i <= 2 * m; i++) {
		for (; pt <= que[i].r; pt++) {
			for (int i = 1; i <= 100; i++)
				g[i][a[pt] % i]++;
			h[a[pt]]++;
		}
		if (que[i].p <= 100)
			que[i].ans = g[que[i].p][que[i].q];
		else
			for (int k = 0; k * que[i].p + que[i].q <= 10000; k++)
				que[i].ans += h[k * que[i].p + que[i].q];
	}
	std::sort(que + 1, que + 2 * m + 1, cmp2);
	for (int i = 1; i <= m; i++)
		printf("%d\n", que[i * 2].ans - que[i * 2 - 1].ans);
}