bzoj3529 [SDOI2014]数表

题目链接

solution

“能同时整除i和j的数”其实就是(gcd(i,j))的因数。

所以题目就是要求

[sumlimits_{g=1,sigma(g)le a}sigma(g)sumlimits_{i=1}^{lfloorfrac{n}{g} floor}sumlimits_{j=1}^{lfloorfrac{m}{g} floor}[gcd(i,j)=1] ]

[=sumlimits_{g=1,sigma(g)le a}sigma(g)sumlimits_{d=1}^{lfloorfrac{n}{g} floor}mu(d)lfloorfrac{n}{dg} floorlfloorfrac{m}{dg} floor ]

(T=gd)

[=sumlimits_{T=1}^nlfloorfrac{n}{T} floorlfloorfrac{m}{T} floorsumlimits_{g|T,sigma(g)le a}sigma(g)mu(frac{T}{g}) ]

(f(i)=sumlimits_{d|i}sigma(d)mu(frac{i}{d}))

后面这部分只有当(sigma(g)le a)时才会产生贡献,如果没有(sigma(g)le a)这个限制的话,我们可以直接先预处理(f)(枚举(g),再枚举(g)的倍数即可),然后前缀和数论分块,搞定。

然后考虑如何处理这个限制,我们可以把询问离线下来,然后按照(a)排序,将(sigma(g))也按照大小排序,从小到大的处理询问,当当前的(g)可以产生贡献时,就更新(f(kg))

然后还要维护前缀和,所以再用个树状数组维护即可。

复杂度(O(nlog^2n+Qsqrt{n}logn))

code

/*
* @Author: wxyww
* @Date:   2020-04-23 10:59:26
* @Last Modified time: 2020-04-23 11:39:46
*/
#include<cstdio>
#include<iostream>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<queue>
#include<vector>
#include<ctime>
using namespace std;
typedef long long ll;
typedef unsigned int Uint;
const int N = 100010;
ll read() {
	ll x = 0,f = 1;char c = getchar();
	while(c < '0' || c > '9') {
		if(c == '-') f = -1; c = getchar();
	}
	while(c >= '0' && c <= '9') {
		x = x * 10 + c - '0'; c = getchar();
	}
	return x * f;
}
ll o[N];

int ans[N],vis[N],prime[N];
Uint mu[N];
struct node {
	int n,m,x,id;
}a[N];
#define pi pair<ll,int>

pi t[N];

bool cmp(const node &A,const node &B) {
	return A.x < B.x;
}

void pre() {
	for(int i = 1;i <= 100000;++i)
		for(int j = i;j <= 100000;j += i)
			o[j] += i;
	
	for(int i = 1;i <= 100000;++i)
		t[i] = make_pair(o[i],i);
	
	mu[1] = 1;
	int tot = 0;
	for(int i = 2;i <= 100000;++i) {
		if(!vis[i]) {
			prime[++tot] = i;
			mu[i] = -1;
		}
		for(int j = 1;j <= tot && prime[j] * i <= 100000;++j) {
			vis[prime[j] * i] = 1;
			if(i % prime[j]) mu[i * prime[j]] = -mu[i];
			else break;
	
		}
	}

}
Uint tree[N];
void update(int pos,Uint c) {

	while(pos <= 100000) {
		tree[pos] += c;
		pos += pos & -pos;
	}
}
Uint query(int pos) {
	Uint ret = 0;
	while(pos) {
		ret += tree[pos];
		pos -= pos & -pos;
	}
	return ret;
}
int main() {
	int Q = read();
	for(int i = 1;i <= Q;++i) {
		a[i].n = read(),a[i].m = read();a[i].x = read();
		a[i].id = i;
	}
	
	pre();
	sort(a + 1,a + Q + 1,cmp);
	sort(t + 1,t + 100001);

	int p = 1;
	for(int i = 1;i <= Q;++i) {

		while(p <= 100000 && t[p].first <= a[i].x) {
			for(int j = t[p].second;j <= 100000;j += t[p].second) {
				update(j,(Uint)t[p].first * mu[j / t[p].second]);
			}
			++p;
		}

		int n = a[i].n,m = a[i].m;
		if(n > m) swap(n,m);
		Uint ret = 0;

		for(int l = 1,r;l <= n;l = r + 1) {
			r = min(n / (n / l),m / (m / l));
			ret += (Uint) (n / l) * (m / l) * (query(r) - query(l - 1));
		}
		ans[a[i].id] = ret;

	}

	for(int i = 1;i <= Q;++i) cout<<(ans[i] & ((1ll << 31) - 1))<<endl;
	return 0;
}
原文地址:https://www.cnblogs.com/wxyww/p/bzoj3529.html