发现其实有关gcd的题目还是挺多的,这里根据做题顺序写出8题。

[bzoj2818: Gcd] gcd(x,y)=质数, 1<=x,y<=n的对数

做这题的时候,懂得了一个非常重要的转化:求(x, y) = k, 1 <= x, y <= n的对数等于求(x, y) = 1, 1 <= x, y <= n/k的对数!所以,枚举每个质数p(线性筛素数的方法见:线性时间内筛素数和欧拉函数),然后求(x, y) = 1, 1 <= x, y <= n/p的个数。

(x, y) = 1的个数如何求呢?其实就是求互质的数的个数。在[1, y]y互质的数有phi(y)个,如果我们令x < y,那么答案就是sigma(phi(y))。因为x, y是等价的,所以答案*2,又因为(1, 1)只有一对,所以-1。最终答案为sigma(sigma(phi(n/prime[i])) * 2 - 1)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
#include <cstdio>
#include <algorithm> const int MAXN = 10000001;
int n, primes;
long long prime[MAXN], phi[MAXN];
bool com[MAXN];
int main()
{
scanf("%d", &n);
for (int i = 2; i <= n; ++i)
{
if (!com[i])
{
prime[primes++] = i;
phi[i] = i-1;
}
for (int j = 0; j < primes && i*prime[j] <= n; ++j)
{
com[i*prime[j]] = true;
if (i % prime[j])
phi[i*prime[j]] = phi[i] * (prime[j]-1);
else
{ phi[i*prime[j]] = phi[i] * prime[j]; break; }
}
}
phi[1] = 1;
for (int i = 2; i <= n; ++i) phi[i] += phi[i-1];
long long ans = 0;
for (int i = 0; i < primes; ++i) ans += phi[n/prime[i]]*2-1;
printf("%lld", ans);
}

[bzoj2190: [SDOI2008]仪仗队] 求gcd(x,y)=1, 0<=x,y<=n的对数

嗯……似乎这题在上一题的时候解决了。不过要注意的是,这题范围是从0开始的,所以,会多出(1, 0)(0, 1)这两组,答案就是sigma(phi(i)) - 1 + 2

[bzoj2005: [Noi2010]能量采集] 求sigma(gcd(x,y)), 1<=x<=n, 1<=y<=m

f[d](x, y) = d的对数,那么答案就是sigma(f[i]*((i-1)*2+1))

f[i]怎么求呢?在1 <= x <= n, 1 <= y <= m中,gcd(x, y) | d的有[n/d] * [m/d]个。不过我们要扣掉所有的倍数,f[i] = [n/d] * [m/d] - f[2i] - f[3i] - f[4i] - ...。逆序做即可。

后来我在想:

  • 为什么上面几题不用这题的方法呢?嗯,因为x, y的取值不一样,这样的话就不得不把莫比乌斯反演搬出来了。
  • 为什么这题不用上面几题的方法呢?嗯,因为这个的复杂度是O(nlogn)O(n/1 + n/2 + ... + n/n) = O(nlogn)),而上面的复杂度只有O(n + n/logn)(质数个数是n/logn级别的)。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include <cstdio>
#include <algorithm> const int MAXN = 100001;
int n, m;
long long f[MAXN];
int main()
{
scanf("%d%d", &n, &m);
if (n > m) std::swap(n, m);
long long ans = 0;
for (int i = n; i >= 1; --i)
{
f[i] = static_cast<long long>(n/i) * (m/i);
for (int j = i+i; j <= n; j += i)
f[i] -= f[j];
ans += f[i]*(2*i-1);
}
printf("%lld", ans);
}

[SPOJ VLATTICE] gcd(i,j,k)=1, 0<=i,j,k<=n 的个数

莫比乌斯反演(Möbius inversion formula)终于出现了!莫比乌斯反演的基本形式就是:

g(n) = sigma(d|n, f(d))
f(n) = sigma(d|n, μ(n/d) * g(d))

还有另外一个形式是:

g(n) = sigma(d|n, f(d))
f(n) = sigma(n|d, μ(n) * g(n/d))

通俗的来说,g(n)f(n)的关系,就是包含仅包含的关系。

g(x)为满足x | (i, j, k)的个数,f(x)为满足(i, j, k) = x的个数。显然g(n) = sigma(d|n, f(d)) = [n/x] * [n/x] * [n/x],所以答案f(1)就可以用下面那个公式算出来了,也就是sigma(μ(d) * [n/d] * [n/d] * [n/d])

不过需要注意的是,这题的范围可以取0,所以我们还需要加上某一维为0的,某两维为0的方案(基本一样啦)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
#include <cstdio>
#include <algorithm> const int MAXN = 1000000;
inline long long sqr(const long long x) { return x * x; }
inline long long cube(const long long x) { return x * x * x; }
int Testcase, n, primes, prime[MAXN+1], mu[MAXN+1];
void GetPrimes()
{
static bool com[MAXN+1];
mu[1] = 1;
for (int i = 2; i <= MAXN; ++i)
{
if (!com[i])
{ prime[primes++] = i; mu[i] = -1; }
for (int j = 0; j < primes && i*prime[j] <= MAXN; ++j)
{
com[i*prime[j]] = true;
if (i % prime[j]) mu[i*prime[j]] = -mu[i];
else { mu[i*prime[j]] = 0; break; }
}
}
}
int main()
{
GetPrimes();
for (scanf("%d", &Testcase); Testcase--; )
{
scanf("%d", &n);
long long ans = 3;
for (int i = 1; i <= n; ++i) ans += mu[i] * cube(n/i);
for (int i = 1; i <= n; ++i) ans += mu[i] * sqr(n/i) * 3;
printf("%lld\n", ans);
}
}

[bzoj1101: [POI2007]Zap] 求有多少对数满足 gcd(x,y)=d, 1<=x<=a, 1<=y<=b

首先肯定想到转化成求gcd(x, y) = 1, 1 <= x <= a/d, 1 <= y <= b/d的对数,和上面一题一样。虽然上面那题复杂度很优,只有O(n),但是对于这边的多组数据完全就无法承受了。

其实,只有O(sqrt(n))个不同的n/i的取值,因此我们可以求mu[i]的前缀和然后分块做!分块可以参考[CQOI2007]余数之和sum bzoj1257

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
#include <cstdio>
#include <algorithm> const int MAXN = 50000;
inline long long sqr(const long long x) { return x * x; }
int Testcase, n, m, d, primes, prime[MAXN+1], mu[MAXN+1], sum[MAXN+1];
void GetPrimes()
{
static bool com[MAXN+1];
mu[1] = 1;
for (int i = 2; i <= MAXN; ++i)
{
if (!com[i])
{ prime[primes++] = i; mu[i] = -1; }
for (int j = 0; j < primes && i*prime[j] <= MAXN; ++j)
{
com[i*prime[j]] = true;
if (i % prime[j]) mu[i*prime[j]] = -mu[i];
else { mu[i*prime[j]] = 0; break; }
}
}
for (int i = 1; i <= MAXN; ++i) sum[i] = sum[i-1] + mu[i];
}
int main()
{
GetPrimes();
for (scanf("%d", &Testcase); Testcase--; )
{
scanf("%d%d%d", &n, &m, &d);
if (n > m) std::swap(n, m);
long long ans = 0;
n /= d, m /= d;
for (int i = 1, last; i <= n; i = last+1)
{
last = std::min(n/(n/i), m/(m/i));
ans += (sum[last]-sum[i-1]) * static_cast<long long>(n/i) * (m/i);
}
printf("%lld\n", ans);
}
}

[bzoj2301: [HAOI2011]Problem b] 求有多少对数满足 gcd(x,y)=k, a<=x<=b, c<=y<=d

和上面那题几乎一样,只是多了x, y的下界。其实这个可以在算方案的时候yy一下,但是总觉得可能会哪里边界算错,于是就是用了一种偷懒的方法:容斥原理。事实上,这种方法并不会比只算一次的方法来的慢多少。

f[a, b]为满足(x, y) = 1, 1 <= x <= a, 1 <= y <= b的对数,那么答案就是f[B/K, D/K] - f[(A-1)/K, D/K] - f[B/K, (C-1)/K] + f[(A-1)/K, (C-1)/K]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
#include <cstdio>
#include <algorithm> const int MAXN = 50000;
inline long long sqr(const long long x) { return x * x; }
int Testcase, A, B, C, D, K, primes, prime[MAXN+1], mu[MAXN+1], sum[MAXN+1];
void GetPrimes()
{
static bool com[MAXN+1];
mu[1] = 1;
for (int i = 2; i <= MAXN; ++i)
{
if (!com[i])
{ prime[primes++] = i; mu[i] = -1; }
for (int j = 0; j < primes && i*prime[j] <= MAXN; ++j)
{
com[i*prime[j]] = true;
if (i % prime[j]) mu[i*prime[j]] = -mu[i];
else { mu[i*prime[j]] = 0; break; }
}
}
for (int i = 1; i <= MAXN; ++i) sum[i] = sum[i-1] + mu[i];
}
long long Process(int n, int m)
{
long long res = 0;
if (n > m) std::swap(n, m);
for (int i = 1, last; i <= n; i = last+1)
{
last = std::min(n/(n/i), m/(m/i));
res += (sum[last]-sum[i-1]) * static_cast<long long>(n/i) * (m/i);
}
return res;
}
int main()
{
GetPrimes();
for (scanf("%d", &Testcase); Testcase--; )
{
scanf("%d%d%d%d%d", &A, &B, &C, &D, &K);
long long ans = Process(B/K, D/K);
ans -= Process((A-1)/K, D/K);
ans -= Process(B/K, (C-1)/K);
ans += Process((A-1)/K, (C-1)/K);
printf("%lld\n", ans);
}
}

[bzoj2820: YY的GCD] 求1<=x<=N, 1<=y<=Mgcd(x,y)为质数有多少对

如果枚举质数的话就要给这个多组数据跪成傻逼了。

ans = sigma(p, sigma(d, μ(d) * (n/pd) * (m/pd)))

Let s = pd, then

ans = sigma(s, sigma(p, μ(s/p) * (n/s) * (m/s)))
= sigma(s, (n/s) * (m/s) * sigma(p, μ(s/p))) Let g(x) = sigma(p, μ(x/p)), then ans = sigma(s, (n/s) * (m/s) * g(s))

如果我们能预处理g(x)的话就能和前面一样分块搞了。这个时候我们多么希望g(x)μ(x)一样是积性函数。看完题解后,发现有一个不是积性函数,胜似积性函数的性质。由于题解没有给证明,所以就意淫了一个证明。

考虑质数p'g(p'x) = sigma(p | p'x, μ(p'x/p))

  • x % p' == 0,那么考虑sigma中的变量p的所有取值,它和g(x)p是相同的。而μ(x)这个函数,如果x有平方因子的话就等于0,因此当p != p'μ(p'x/p) = 0,当p == p'是,μ(p'x/p) = μ(x)。所以g(p'x) = μ(x)
  • x % p' != 0,同样考虑p,会发现它的取值只比g(x)中的p多出一个p'。同理按照p是否等于p'讨论,可以得到g(p'x) = -g(x) + μ(x)

因此g(x)可以在线性筛素数的时候算出。剩下的就是前缀和、分块了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
#include <cstdio>
#include <algorithm> const int MAXN = 10000000;
int Testcase, n, m, primes, prime[MAXN+1], mu[MAXN+1], g[MAXN+1], sum[MAXN+1];
void GetPrimes()
{
static bool com[MAXN+1];
mu[1] = 1;
for (int i = 2; i <= MAXN; ++i)
{
if (!com[i])
prime[primes++] = i, mu[i] = -1, g[i] = 1;
for (int j = 0; j < primes && i*prime[j] <= MAXN; ++j)
{
com[i*prime[j]] = true;
if (i % prime[j]) mu[i*prime[j]] = -mu[i], g[i*prime[j]] = -g[i] + mu[i];
else { mu[i*prime[j]] = 0, g[i*prime[j]] = mu[i]; break; }
}
}
for (int i = 1; i <= MAXN; ++i) sum[i] = sum[i-1] + g[i];
}
int main()
{
GetPrimes();
for (scanf("%d", &Testcase); Testcase--; )
{
scanf("%d%d", &n, &m);
if (n > m) std::swap(n, m);
long long ans = 0;
for (int i = 1, last; i <= n; i = last+1)
{
last = std::min(n/(n/i), m/(m/i));
ans += (n/i) * static_cast<long long>(m/i) * (sum[last]-sum[i-1]);
}
printf("%lld\n", ans);
}
}

[bzoj2705: [SDOI2012]Longge的问题] 求 sigma(gcd(i,n), 1<=i<=n<2^32)

又是令gcd(i, n) = d,答案就是sigma(phi(n/d)),但是我们不能预处理出phi[]数组,因为开不了数组……

注意到因数个数是O(2sqrt(n))级别的,我们枚举所有的n/d,一边dfs一边算phi。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
#include <cmath>
#include <cstdio> int n, cnt, p[30], c[30];
long long ans = 0;
void dfs(const int step, int pdt, int phi)
{
if (step == cnt)
{
ans += phi;
return;
}
dfs(step+1, pdt, phi);
phi = phi / p[step] * (p[step]-1);
for (int i = 1; i <= c[step]; ++i)
dfs(step+1, pdt *= p[step], phi);
}
int main()
{
scanf("%d", &n);
int x = n;
for (int i = 2; i*i <= x; ++i)
if (x % i == 0)
{
for (; x % i == 0; x /= i) ++c[cnt];
p[cnt++] = i;
}
if (x > 1) c[cnt] = 1, p[cnt++] = x;
dfs(0, 1, n);
printf("%lld\n", ans);
}

(转载)关于gcd的8题的更多相关文章

  1. [转载]sql语句练习50题

    Student(Sid,Sname,Sage,Ssex) 学生表 Course(Cid,Cname,Tid) 课程表 SC(Sid,Cid,score) 成绩表 Teacher(Tid,Tname) ...

  2. 【转载】 HDU 动态规划46题【只提供思路与状态转移方程】

    1.Robberies 连接 :http://acm.hdu.edu.cn/showproblem.php?pid=2955      背包;第一次做的时候把概率当做背包(放大100000倍化为整数) ...

  3. 转载 - 最短路&差分约束题集

    出处:http://blog.csdn.net/shahdza/article/details/7779273 最短路 [HDU] 1548    A strange lift基础最短路(或bfs)★ ...

  4. 数论,质因数,gcd——cf1033D 好题!

    直接筛质数肯定是不行的 用map<ll,ll>来保存质因子的指数 考虑只有3-5个因子的数的组成情况 必定是a=pq or a=p*p or a=p*p*p or a=p*p*p*p 先用 ...

  5. CodeForces - 1047CEnlarge GCD(这题很难,快来看题解,超级详细,骗浏览量)

    C. Enlarge GCD time limit per test1 second memory limit per test256 megabytes inputstandard input ou ...

  6. ACM 刷题小技巧【转】

    转载自URl-team ACM做题过程中的一些小技巧. 1.一般用C语言节约空间,要用C++库函数或STL时才用C++; cout.cin和printf.scanf最好不要混用. 大数据输入输出时最好 ...

  7. [NOIp 2009]Hankson的趣味题

    Description Hanks 博士是 BT (Bio-Tech,生物技术) 领域的知名专家,他的儿子名叫 Hankson.现在,刚刚放学回家的 Hankson 正在思考一个有趣的问题. 今天在课 ...

  8. 【HDU4947】GCD Array (莫比乌斯反演+树状数组)

    BUPT2017 wintertraining(15) #5H HDU- 4947 题意 有一个长度为l的数组,现在有m个操作,第1种为1 n d v,给下标x 满足gcd(x,n)=d的\(a_x\ ...

  9. 【转】POJ百道水题列表

    以下是poj百道水题,新手可以考虑从这里刷起 搜索1002 Fire Net1004 Anagrams by Stack1005 Jugs1008 Gnome Tetravex1091 Knight ...

随机推荐

  1. ASP.NET MVC 开发中遇到的两个小问题

    最近在做一个网站,用asp.net MVC4.0来开发,今天遇到了两个小问题,通过查找相关渠道解决了,在这里把这两个问题写出来,问题非常简单,不喜勿喷,mark之希望可以给遇到相同问题的初学者一点帮助 ...

  2. .NET中的弱引用

    弱引用是什么? 要搞清楚什么是弱引用,我们需要先知道强引用是什么.强引用并不是什么深奥的概念,其实我们平时所使用的.Net引用就是强引用.例如: Cat cat = new Cat(); 变量cat就 ...

  3. Oracle监听器—动态注册

    注册就是将数据库作为一个服务注册到监听程序.客户端不需要知道数据库名和实例名,只需要知道该数据库对外提供的服务名就可以申请连接到数据库.这个服务名可能与实例名一样,也有可能不一样. 注册分: 1. 静 ...

  4. Flasback数据库(闪回数据库)

    数据库闪回原理: 一旦启用了闪回数据库,修改的块映像会不时从数据库缓冲区复制到闪回缓冲区.通过新的后台恢复写入器(Recovery Writer, RVWR)将此闪回缓冲区内容刷新到磁盘和闪回日志接着 ...

  5. 一、mysql使用入门

    mysql -h localhost -u root -p123456 登录mysql服务器 show databases 列出所拥有的数据库 use www 选择一个www的数据库 show tab ...

  6. 通过telnet命令进行网络邮件发送

    1.建立smtp邮箱服务连接 open smtp.sina.com 2.连接上邮箱服务后进行握手操作 helo smtp.sina.com 3.输入帐号密码进行验证::此步后缓冲区会输出一些字符,你只 ...

  7. 【BZOJ2428】[HAOI2006]均分数据

    Description 已知N个正整数:A1.A2.…….An .今要将它们分成M组,使得各组数据的数值和最平均,即各组的均方差最小.均方差公式如下: ,其中σ为均方差,是各组数据和的平均值,xi为第 ...

  8. 微软职位内部推荐-Sr DEV Lead, Bing Search Relevance

    微软近期Open的职位: Contact Person: Winnie Wei (wiwe@microsoft.com )Sr DEV Lead, Bing Search RelevanceLocat ...

  9. JAVA多线程synchronized详解

    Java语言的关键字,当它用来修饰一个方法或者一个代码块的时候,能够保证在同一时刻最多只有一个线程执行该段代码. 当两个并发线程访问同一个对象object中的这个synchronized(this)同 ...

  10. REST Design Concerns

    Less Requests, More data; one of the core RESTful API design paradigms is the concept of less API re ...