【CF1553F】Pairwise Modulo - 树状数组

题目描述

You have an array \(a\) consisting of \(n\) distinct positive integers, numbered from \(1\) to \(n\) . Define \(p_k\) as

\[p_k = \sum_{1 \le i, j \le k} a_i \bmod a_j, \]

where \(x \bmod y\) denotes the remainder when \(x\) is divided by \(y\) . You have to find and print \(p_1, p_2, \ldots, p_n\).

思路

首先考虑知道前 \(i-1\) 个数的 \(p_{i-1}\),新增一个 \(a_i\) 会增加 \(\sum_{j=1}^{i-1}a_j \bmod a_i+a_i \bmod a_j\),自然地想到将 \(\bmod\) 改为下取整形式,得到

\[p_i=p_{i-1}+\sum_{j=1}^{i-1}a_j-\lfloor\frac{a_j}{a_i}\rfloor\cdot a_i+a_i -\lfloor\frac{a_i}{a_j}\rfloor\cdot a_j \]

\(s\)\(a\) 的前缀和,即 \(s_i=\sum_{j=1}^{i}a_j\),则

\[p_i=p_{i-1}+s_{i-1}+(i-1)a_i-a_i\cdot\sum_{j=1}^{i-1}\lfloor\frac{a_j}{a_i}\rfloor-\sum_{j=1}^{i-1}\lfloor\frac{a_i}{a_j}\rfloor\cdot a_j \]

对于 \(a_i\cdot\sum_{j=1}^{i-1}\lfloor\frac{a_j}{a_i}\rfloor\),当 \(a_j\in[d\cdot a_i,(d+1)\cdot a_i)\) 时,\(a_j\)\(a_i\)\(d\) 的贡献,用树状数组维护 \(a_j\),枚举 \(d\) 对区间 \([d\cdot a_i,(d+1)\cdot a_i)\) 求和,然后单点修改插入 \(a_i\).

对于 \(\sum_{j=1}^{i-1}\lfloor\frac{a_i}{a_j}\rfloor\cdot a_j\),当 \(a_i\in[d\cdot a_j,(d+1)\cdot a_j)\) 时,\(a_j\)\(a_i\)\(a_j\cdot d\) 的贡献,用树状数组维护每个 \(a_j\) 对其他位置贡献,单点查询 \(a_i\),然后枚举 \(d\) 区间修改,将区间 \([d\cdot a_i,(d+1)\cdot a_i)\) 加上 \(a_i\cdot d\).

刚做这题时一直在想除法分块,除法分块是考虑 \(\lfloor\frac{n}{i}\rfloor\) 中,\(n\) 一定时 \(i\) 在某些范围内会使下取整的值相同,这样做的话只能处理 \(\sum_{j=1}^{i-1}\lfloor\frac{a_i}{a_j}\rfloor\cdot a_j\) 这个式子,因为另一个式子的分子是 \(a_j\) 是不定的. 而本题则换了个思路,考虑分子是什么范围内时有相同的贡献.

枚举 \(d\) 看上去很暴力,对于一个 \(a_i\) 会枚举 \(\lfloor\frac{3\cdot 10^5}{a_i}\rfloor\) 次,但是由于 \(a_i\) 互不相同,所以复杂度是调和级数级别的.

#include <algorithm>
#include <cstdio>
#define int long long
using namespace std;
const int maxn = 3e5 + 10;
int n,pre,ans;
struct fenwick {
	int c[maxn];
	inline void add(int x,int v) {
		for (;x <= maxn;x += x&(-x)) c[x] += v;
	}
	inline int sum(int x) {
		int res = 0;
		for (;x;x -= x&(-x)) res += c[x];
		return res;
	}
} q,p;
signed main() {
	scanf("%lld",&n);
	for (int i = 1,x;i <= n;i++) {
		scanf("%lld",&x);
		ans += pre+(i-1)*x-p.sum(x);
		pre += x;
		for (int d = 1;d*x <= maxn;d++)
			ans -= x*d*(q.sum(min((d+1)*x,maxn)-1)-q.sum(d*x-1));
		printf("%lld%c",ans,i ^ n ? ' ' : '\n');
		q.add(x,1);
		for (int d = 1;d*x <= maxn;d++) {
			p.add(d*x,d*x);
			p.add(min((d+1)*x,maxn),-d*x);
		}
	}
	return 0;
}
posted @ 2021-07-27 15:48  lrj124  阅读(73)  评论(0编辑  收藏  举报