window.cnblogsConfig = {//可以放多张照片,应该是在每一个博文上面的图片,如果是多张的话,那么就随机换的。 homeTopImg: [ "https://cdn.luogu.com.cn/upload/image_hosting/clcd8ydf.png", "https://cdn.luogu.com.cn/upload/image_hosting/clcd8ydf.png" ], }

P1447 [NOI2010] 能量采集

题目传送

容斥思想的一道好题。

题目容易转化为:

\[2\times \sum_{i=1}^n \sum_{j=1}^n (\gcd(i, j))\ - nm. \]

直接求和不好求,不妨转换为枚举 \(d=\gcd(i,j)\)

那么 \(i,j\) 应该均为 \(d\) 的倍数。记 \(f(i)=\left \lfloor \frac{n}{i} \right \rfloor \cdot \left \lfloor \frac{m}{i} \right \rfloor\),但发现会有虽然都是 \(i\) 的倍数,但 \(\gcd\) 并不是 \(i\) 的数,考虑如何去除。

\(g(i)\)\(\gcd(x,y)=i\) 的数量,容易发现 \(f(i)={\textstyle \sum \limits_{j=1}^{\left \lfloor \frac{n}{i} \right \rfloor} g(i\cdot j)}\)

\(f(i)\) 是好求的,我们倒着处理 \(g(i)\)。复杂度是调和级数 \(\mathcal O(n\log n)\)

#include <bits/stdc++.h>
using namespace std;
#define int long long
#define pb push_back
#define eb emplace_back
#define all(x) (x).begin(),(x).end()
#define fi first
#define se second
#define SZ(x) ((int)(x).size())
typedef vector<int> VI;
typedef long long ll;
typedef pair<int,int> PII;
const ll MOD=1e9+7;
// head
const int N=1e5+5;
int f[N],g[N];
signed main() 
{
    cin.tie(nullptr);
    ios::sync_with_stdio(false);

    int n,m;cin>>n>>m;
    int nn=min(n,m);
    for(int i=1;i<=nn;i++) f[i]=(n/i)*(m/i);
    for(int i=nn;i>=1;i--){
        int cnt=0;
        for(int j=i*2;j<=nn;j+=i){
            cnt+=g[j];
        }
        g[i]=f[i]-cnt;
    }
    int ans=0;
    for(int i=1;i<=nn;i++) ans+=g[i]*i;
    cout<<2*ans-m*n<<endl;
}
posted @ 2024-08-05 19:54  ziyistudy  阅读(32)  评论(0)    收藏  举报