do_while_true

一言(ヒトコト)

Codeforces 1207F Remainder Problem

\(\mathcal{Solution}\)

其实根号分治就可以,这个人傻乎乎地写的树状数组。

\(sum_{i,j,k}\) 为模 \(i\)\(j\) 的答案前缀和的树状数组。

可以有两种方法来处理询问和修改:

  1. 树状数组。

  2. 暴力一个一个跳。

因为有的 \(x\) 很大的时候暴力跳的次数很少,复杂度是对的,所以要设置一个分界线,在分界线下的用树状数组,在分界线上的暴力。

设这个分界线为 \(M\),对于树状数组修改一次的复杂度为 \(\mathcal{O}(M\log n )\),暴力依次跳的复杂度为 \(\mathcal{O}(\frac{n}{M})\),总复杂度就是 \(\mathcal{O}(nM\log n +\frac{n^2}{M})\) 这样子。其实是可以卡满的,但是在不超过空间限制的前提下调一下 \(M\) 即可,跑CF的 main test 还是很快的。

\(\mathcal{Code}\)

其实二维数组记录就可以,没有必要树状数组,仅提供一个思路,代码参考。

#include<iostream>
#include<cstdio>
#define re register
#define ll long long
inline int read() {
	re int r = 0; re bool w = 0; re char ch = getchar();
	while(ch < '0' || ch > '9') w = ch == '-' ? 1 : w, ch = getchar();
	while(ch >= '0' && ch <= '9') r = (r << 3) + (r << 1) + (ch ^ 48), ch = getchar();
	return w ? ~r + 1 : r;
}
inline int lowbit(int x) { return x & (-x); }
inline int Max(int x, int y) { return x > y ? x : y; }
inline int Min(int x, int y) { return x < y ? x : y; }
const int M = 10;
const int N = 500010;
ll sum[M + 1][M + 1][N];
int n, optt[N], xx[N], yy[N], a[N]; 
//sum[i][j][k] 模i为j的前k个的前缀和的tree 
void modify(int i, int j, int k, int val) {
	while(k <= n) {
		sum[i][j][k] += val;
		k += lowbit(k);
	}
}
ll query(int i, int j, int k) {
	re ll sumq = 0;
	while(k) {
		sumq += sum[i][j][k];
		k -= lowbit(k);
	}
	return sumq;
}
int main() {
	int q = read();
	for(int i = 1; i <= q; ++i) optt[i] = read(), xx[i] = read(), yy[i] = read(), n = Max(n, xx[i]);
	re int opt, x, y;
	for(int Q = 1; Q <= q; ++Q) {
		opt = optt[Q], x = xx[Q], y = yy[Q];
		if(opt == 1) {
			a[x] += y;
			for(int i = 1; i <= M; ++i) {
				modify(i, x % i, x / i + (x % i > 0), y);
			}
		}
		else {
			re ll sumq = 0;
			if(x >= M + 1) {
				for(int i = y; i <= n; i += x) sumq += a[i];
			}
			else {
				sumq = query(x, y, n);
			}
			printf("%lld\n", sumq);
		}
	}
	return 0;
}
posted @ 2020-10-25 18:13  do_while_true  阅读(83)  评论(0编辑  收藏  举报