[SDOI2014]数表

题目链接:[SDOI2014]数表

题意:

\(T\) 组数据,每次给出 \(n,m,a\) ,在 \(n*m\) 矩阵的每个位置上有一个数值,第 \(i\) 行第 \(j\) 列数值为能同时整除 \(i\)\(j\) 的所有自然数之和,求 \(n*m\) 矩阵中所有不超过 \(a\) 的数值和。\((T\leq2*10^4;1\leq n,m\leq 10^5)\)


首先不考虑小于等于 \(a\) 的限制。

能同时整除 \(i\)\(j\) 就是能整除 \(gcd(i,j)\),所有每个位置上的数即为 \(gcd\) 的因子和 \(\sigma(gcd(i,j))\)

\(f(x)\)表示 \(x\)\(n,m\) 数表中出现次数。

\(f(x)=\sum\limits_{i=1}^n\sum\limits_{j=1}^m[gcd(i,j)=x]\)

老朋友了。。。

\(F(x) = \sum\limits_{x\vert d}f(d)=\left\lfloor\dfrac nx\right\rfloor\left\lfloor\dfrac mx\right\rfloor\)

\(f(x)=\sum\limits_{x\vert d}\mu(\dfrac dx)F(d) = \sum\limits_{x\vert d}\mu(\dfrac d x)\left\lfloor\dfrac nx\right\rfloor\left\lfloor\dfrac mx\right\rfloor\)

答案为每一个 \(x\) 约数和乘以出现次数。

\(Ans = \sum\limits_{i=1}^n\sigma(i)f(i)\)

\(\qquad=\sum\limits_{i=1}^n\sigma(i)\sum\limits_{i\vert d}\mu(\frac di)\left\lfloor\frac nd\right\rfloor\left\lfloor\frac md\right\rfloor\)

将枚举 \(i\) 的倍数改为枚举 \(d\) 的因数。

\(Ans=\sum\limits_{d=1}^n\left\lfloor\frac nd\right\rfloor\left\lfloor\frac md\right\rfloor\sum\limits_{i\vert d} \sigma(i)\mu(\frac di)\)

前面可以用整除分块,后面做前缀和。

\(g(x)=\sum\limits_{i\vert x}\sigma(i)\mu(\frac xi)\)

\(g\) 做个前缀和,分块的时候乘上前面的 \(\left\lfloor\frac nd\right\rfloor\left\lfloor\frac md\right\rfloor\)


然后考虑有 \(a\) 的限制,则对于 \(g(x)\) 来讲当 \(x\) 的因子 \(i\) 满足\(\sigma(i)\ge a\) 时其对 \(g\) 没有贡献。

将询问离线按 \(a\) 升序排列,每当 \(a\) 增加时就将 \(\sigma(i)=a\) 的贡献加上,可以枚举倍数来更新 \(g\) 的值,由于 \(g\) 值在变化且需要快速求出前缀和,值需要用树状数组动态维护。

瓶颈复杂度为\(O(T\sqrt{n}log(n))\)。竟然可过?真神奇~

#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdio>
#include <cmath>
#define QWQ cout<<"QwQ"<<endl;
#define uint unsigned int
#include <vector>
#include <queue>
#include <stack>
#include <map>
using namespace std;
const uint N=101010;
const uint qwq=303030;
const uint inf=0x3f3f3f3f;

uint T;
uint n,m;
uint big;
uint vis[N],p[N],cnt,mu[N];
uint f[N],g[N];
uint tree[N];
uint ans[N];
struct E{ uint zhi,id; } s[N];
inline bool cmpe(E aa,E bb) { return aa.zhi < bb.zhi; }
struct D{ uint x,y,a,id; } q[N];
inline bool cmpd(D aa,D bb) { return aa.a < bb.a; }

inline uint read() {
	uint sum1 = 0, ff = 1; char c = getchar();
	while(c<'0' || c>'9') { if(c=='-') ff = -1; c = getchar(); }
	while(c>='0'&&c<='9') { sum1 = sum1 * 10 + c - '0'; c = getchar(); }
	return sum1 * ff;
}

void qiu(uint h) {
	mu[1] = f[1] = 1;
	for(uint i=2;i<=h;i++) {
		if(!vis[i]) {
			p[++cnt] = i; mu[i] = -1;
			f[i] = i+1; g[i] = 1;
		}
		for(uint j=1; j<=cnt && i*p[j]<=h; j++) {
			uint now = i*p[j];
			vis[now] = 1;
			if(i%p[j]==0) {
				f[now] = f[i] * p[j] + g[i];
				g[now] = g[i];
				break;
			}
			f[now] = f[i] * (p[j]+1);
			g[now] = f[i];
			mu[now] = -mu[i];
		}
	}
	for(uint i=1;i<=h;i++) s[i].id = i, s[i].zhi = f[i];
	sort(s+1,s+h+1,cmpe);
}

inline void add(uint we,uint z) { for(uint i=we;i<=big;i+=(i&-i)) tree[i] += z; }
inline uint ask(uint we) { uint res = 0; for(uint i=we;i;i-=(i&-i)) res += tree[i]; return res; }

inline void insert(uint d) {
	for(uint i=1;i*d<=big;i++) add(i*d,f[d]*mu[i]);
}

int main() {
	qiu(big=N-1000);
	T = read();
	for(uint i=1;i<=T;i++) q[i].x = read(), q[i].y = read(), q[i].a = read(), q[i].id = i;
	sort(q+1,q+T+1,cmpd);
	uint nowa = 0, nows = 1;
	for(uint i=1;i<=T;i++) {
		while(nowa < q[i].a) {
			nowa++;
			while(s[nows].zhi <= nowa) insert(s[nows++].id);
		}
		n = q[i].x; m = q[i].y;
		if(n>m) swap(n,m);
		uint last = 0, now = 0;
		for(uint l=1,r;l<=n;l=r+1) {
			r = min( n/(n/l), m/(m/l) );
			now = ask(r);
			ans[ q[i].id ] += (n/l) * (m/l) * (now-last);
			last = now;
		}
	}
	uint tot = (1<<31);
	for(uint i=1;i<=T;i++) cout<<ans[i]%tot<<"\n";
	return 0;
}

细节还蛮多的,但是一A了,有被爽到~。。。


彩蛋:

由于此题答案要对\(2^{31}\)取模:

159巨佬TQL!orz

posted @ 2020-04-21 18:23  maple276  阅读(129)  评论(1编辑  收藏  举报