<[Sdoi2014]数表>,<Sdoi14R1D1T1 Table> - JoishBadeR - Holy Sh*t

<[Sdoi2014]数表>,<Sdoi14R1D1T1 Table>

JoishBadeR posted @ 2014年4月15日 14:34 in Math with tags math bzoj , 1313 阅读

Description:

一个N×M的数表中第i行第j列上的数定义为

有多组询问,每次询问数表中不超过A的元素和。

 

Solution:

定义数表中第i行第j列的数是a[i][j],很容易发现a[i][j] = a[d][d] (d = gcd(i, j))

那么我们要求的答案就是f(d)表示的是d的约数和

变换一下得到

卷积一下,到这里我们就得到了一个的算法

考虑离线做法

如果我们已知了F(t)的前缀和的话,显然可以每次根号N地求出答案

同时我们有f(d) <= A的限制。

离线按照A排序,用树状数组维护F(t)的前缀和。

从询问(N,M,A) -> 询问(N',M',A')时,我们需要把所有的值在(A,A']内的f(d)全部插入进去,

显然一个f(d)只会被插入一次,一个f(d)可以更新的F(t)是logN的。

这样我们就得到了一个的做法

 

#include<cstdio>
#include<algorithm>
#include<iostream>
using namespace std;
const int MAXN = 100010;
pair<int, int> f[MAXN];
int a[MAXN], Answer[MAXN];
int T, N, M, A; bool vis[MAXN];
int divisor[MAXN][200], cnt[MAXN];
int prime[MAXN], miu[MAXN], tot;
struct Tque
{
	int N, M, A, id;
	Tque() {}
	Tque(int _id, int _N, int _M, int _A)
	{ id = _id; N = _N; M = _M; A = _A; }	
} Que[MAXN];
bool cmpA(Tque a, Tque b) { return a.A < b.A; }
void Modify(int x, int y) { for(; x <= 100000; x += x & -x) a[x] += y; }
int Query(int x) { int ret = 0; for(; x; x -= x & -x) ret += a[x]; return ret; }
int main()
{
	freopen("table.in", "r", stdin);
	freopen("table.out", "w", stdout);

	for(int i = 1; i <= 100000; ++i)
		for(int j = 1; j <= 100000; ++j)
		{
			if((long long) i * j > 100000) break;
			divisor[i * j][cnt[i * j]++] = i;
		}
	for(int i = 1; i <= 100000; ++i)
		for(int j = 0; j < cnt[i]; ++j) f[i].first += divisor[i][j];
	for(int i = 2; i <= 100000; ++i)
	{
		f[i].second = i;
		if(!vis[i]) prime[tot++] = i, miu[i] = -1;
		for(int j = 0; j < tot; ++j)
		{
			if((long long) i * prime[j] > 100000) break;
			vis[i * prime[j]] = 1; miu[i * prime[j]] = -miu[i];
			if(i % prime[j] == 0) { miu[i * prime[j]] = 0; break; }
		}
	}
	scanf("%d", &T); miu[1] = f[1].second = 1; sort(f + 1, f + 100001); 
	for(int i = 1, x, y, z; i <= T; ++i)
		scanf("%d%d%d", &x, &y, &z), Que[i] = Tque(i, x, y, z);
	sort(Que + 1, Que + T + 1, cmpA);
	for(int i = 1, p = 1; i <= T; ++i)
	{
		int Lim = min(N = Que[i].N, M = Que[i].M), ans = 0; A = Que[i].A;
		for(int d = f[p].second; p <= 100000 && f[p].first <= A; d = f[++p].second)
			for(int k = 1; (long long) d * k <= 100000; ++k) Modify(d * k, miu[k] * f[p].first);
		for(int l = 1, r; l <= Lim; l = r + 1)
			Answer[Que[i].id] += (Query(r = min(N / (N / l), M / (M / l))) - Query(l - 1)) * (N / l) * (M / l);
	}
	for(int i = 1; i <= T; ++i) printf("%d\n", Answer[i] & 0x7fffffff);
	return 0;
}

 

Hint:

考场上只想出60分的暴力= =根本没想去离线。。真是太悲伤了。。

卧槽latex代码在这里竟然没用(?)一张一张写出来截图真是蛋疼= =


登录 *


loading captcha image...
(输入验证码)
or Ctrl+Enter
Host by is-Programmer.com | Power by Chito 1.3.3 beta | © 2007 LinuxGem | Design by Matthew "Agent Spork" McGee