码迷,mamicode.com
首页 > 其他好文 > 详细

P2522 [HAOI2011]Problem b

时间:2019-01-20 10:30:36      阅读:204      评论:0      收藏:0      [点我收藏+]

标签:play   uid   图片   c++   void   oid   \n   ==   dig   

\(\color{#0066ff}{ 题目描述 }\)

对于给出的n个询问,每次求有多少个数对(x,y),满足a≤x≤b,c≤y≤d,且gcd(x,y) = k,gcd(x,y)函数为x和y的最大公约数。

\(\color{#0066ff}{输入格式}\)

第一行一个整数n,接下来n行每行五个整数,分别表示a、b、c、d、k

\(\color{#0066ff}{输出格式}\)

共n行,每行一个整数表示满足要求的数对(x,y)的个数

\(\color{#0066ff}{输入样例}\)

2
2 5 1 5 1
1 5 1 5 2

\(\color{#0066ff}{输出样例}\)

14
3

\(\color{#0066ff}{数据范围与提示}\)

100%的数据满足:1≤n≤50000,1≤a≤b≤50000,1≤c≤d≤50000,1≤k≤50000

\(\color{#0066ff}{ 题解 }\)

abcd的范围有些恶心,考虑拆开算

显然可以当做二维前缀和一样的东西处理

因此转化为求

\[ \sum_{i=1}^n\sum_{j=1}^m [gcd(i,j)==k] \]

把k除过去

\[ \sum_{i=1}^{\lfloor \frac n k \rfloor} \sum_{j=1}^{\lfloor \frac m k \rfloor} [gcd(i,j)==1] \]

因为有

\[ e(n)=[n==1] \]

\[ \mu*i=e \]

\[ i(n)=1 \]

所以

\[ e(n)=\sum_{d|n} \mu(d)*i(\frac n d)=\sum_{d|n} \mu(d) \]

因此原式可以化成

\[ \sum_{i=1}^{\lfloor \frac n k \rfloor} \sum_{j=1}^{\lfloor \frac m k \rfloor} \sum_{d|gcd(i,j)} \mu(d) \]

\[ d|gcd(i,j)\to d|i ,d|j \]

i和j都是d的倍数,考虑枚举d,那么有多少i,j满足是d的倍数呢?

原式变成

\[ \sum_{d=1}^{min(\lfloor \frac n k \rfloor,\lfloor \frac m k \rfloor)} \mu(d) *\lfloor \frac{n}{kd}\rfloor * \lfloor \frac{m}{kd}\rfloor \]

\(\mu\)前缀和一下,然后后面整数分块搞一搞就行了

还有懵逼鸟斯推法这是什么??

技术分享图片

来自--y2823774827y

#include<bits/stdc++.h>
#define LL long long
LL in() {
    char ch; LL x = 0, f = 1;
    while(!isdigit(ch = getchar()))(ch == '-') && (f = -f);
    for(x = ch ^ 48; isdigit(ch = getchar()); x = (x << 1) + (x << 3) + (ch ^ 48));
    return x * f;
}
const int maxn = 1e5 + 10;
int k;
int pri[maxn], mu[maxn], tot;
bool vis[maxn];
void predoit() {
    mu[1] = 1;
    for(int i = 2; i <= 50505; i++) {
        if(!vis[i]) pri[++tot] = i, mu[i] = -1;
        for(int j = 1; j <= tot && i * pri[j] <= 50505; j++) {
            vis[i * pri[j]] = true;
            if(i % pri[j] == 0) break;
            else mu[i * pri[j]] = -mu[i];
        }
    }
    for(int i = 2; i <= 50505; i++) mu[i] += mu[i - 1];
}
LL work(int n, int m) {
    n /= k, m /= k;
    LL ans = 0;
    for(int l = 1, r; l <= std::min(n, m); l = r + 1) {
        r = std::min(n / (n / l), m / (m / l));
        ans += (LL)(n / l) * (m / l) * (mu[r] - mu[l - 1]);
    }
    return ans;
}
int main() {
    predoit();
    for(int T = in(); T --> 0;) {
        int a = in(), b = in(), c = in(), d = in();
        k = in();
        printf("%lld\n", work(b, d) - work(a - 1, d) - work(b, c - 1) + work(a - 1, c - 1));
    }
    return 0;
}

P2522 [HAOI2011]Problem b

标签:play   uid   图片   c++   void   oid   \n   ==   dig   

原文地址:https://www.cnblogs.com/olinr/p/10293961.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!