123789456ye

已AFO

[SDOI2014]数表

题面:Luogu
题解:推式子
先不管限制条件

\[\begin{split} ans &= \sum_{i=1}^{n}{\sum_{j=1}^{m}{\sigma(\gcd(i,j))}} \\ &= \sum_{d=1}^{n}{\sigma(d)\sum_{i=1}^{\frac{n}{d}}{\sum_{j=1}^{\frac{m}{d}}{[\gcd(i,j)=1]}}} \\ &= \sum_{d=1}^{n}{\sigma(d)\sum_{x=1}^{\frac{n}{d}}{\mu(x)\frac{n}{xd}\frac{m}{xd}}} \\ &= \sum_{T=1}^{n}{\frac{n}{T}\frac{m}{T}\sum_{d|T}{\sigma(d)\mu(\frac{T}{d})}} \end{split} \]

加上限制条件

\[ans=\sum_{T=1}^{n}{\frac{n}{T}\frac{m}{T}\sum_{d|T}{\sigma(d)\mu(\frac{T}{d})}}[\sigma(d)\le a] \]

于是考虑将询问离线,按\(a\)排序
同时也要将\(\sigma(x)\)离线,可以用树状数组维护更新(单点加,区间和)
除了推式子套了一个数据结构之外,其他的其实还好 毕竟推式子套路多啊
复杂度:\(O(T\sqrt n\log n+n\log^2n)\)

#include<bits/stdc++.h>
using namespace std;
inline void read(int& x)
{
	x = 0; char c = getchar();
	while (!isdigit(c)) c = getchar();
	while (isdigit(c)) x = x * 10 + c - '0', c = getchar();
}
#define maxn 100005
#define N 100000
namespace Bitarray
{
	int c[maxn];
	inline int lowbit(const int& x) { return x & (-x); }
	inline void update(int pos, int val) { for (int i = pos; i <= N; i += lowbit(i)) c[i] += val; }
	inline int query(int pos) { int ans = 0; for (int i = pos; i; i -= lowbit(i)) ans += c[i]; return ans; }
};
using namespace Bitarray;
struct Query
{
	int n, m, a, id;
	bool operator < (const Query& p) const { return a < p.a; }
}q[maxn], f[maxn];
int prime[maxn], vis[maxn], pnum, miu[maxn], ans[maxn], d[maxn];
void eular()
{
	miu[1] = 1;
	for (int i = 2; i <= N; ++i)
	{
		if (!vis[i]) prime[++pnum] = i, miu[i] = -1;
		for (int j = 1; j <= pnum && i * prime[j] <= N; ++j)
		{
			vis[i * prime[j]] = 1;
			if (i % prime[j]) miu[i * prime[j]] = -miu[i];
			else break;
		}
	}
	for (int i = 1; i <= N; ++i)
		for (int j = i; j <= N; j += i) d[j] += i;
	for (int i = 1; i <= N; ++i) f[i].a = d[i], f[i].id = i;
	sort(f + 1, f + N + 1);
}
inline void solve(Query& x)
{
	int mn = min(x.n, x.m);
	int& Ans = ans[x.id];
	for (int l = 1, r; l <= mn; l = r + 1)
	{
		r = min(x.n / (x.n / l), x.m / (x.m / l));
		Ans += (x.n / l) * (x.m / l) * (query(r) - query(l - 1));
	}
}
inline void Update(int x)
{
	for (int i = 1; i * x <= N; ++i) update(i * x, miu[i] * d[x]);
}
int main()
{
	int T; read(T); eular();
	for (int i = 1; i <= T; ++i) read(q[i].n), read(q[i].m), read(q[i].a), q[i].id = i;
	sort(q + 1, q + T + 1);
	for (int i = 1, r = 0; i <= T; ++i)
	{
		while (f[r + 1].a <= q[i].a && r < N) Update(f[++r].id);
		solve(q[i]);
	}
	for (int i = 1; i <= T; ++i) printf("%d\n", ans[i] & ((1 << 31) - 1));//%2^31就直接自然溢出
	return 0;
}
posted @ 2020-03-22 20:15  123789456ye  阅读(130)  评论(0编辑  收藏  举报