洛谷 1447 [NOI2010]能量采集——容斥/推式子

题目:https://www.luogu.org/problemnew/show/P1447

1.容斥原理

  求 f [ i ] 表示 gcd==i 的对数,先 f [ i ] = (n/i) * (m/i),再考虑减去不合法的对数。

  不合法就是不互质,也就是还有别的公因数,即还能再除。直接算会重复,不如限定求出 gcd==j 的对数。

  利用更大的 f [ ] 即可。在 n/i 和 m/i 的基础上 gcd==j 的对数就是 f [ i*j ]。所以要倒推。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define ll long long
using namespace std;
const int N=1e5+5;
int n,m;
ll ans,f[N],s[N];
int main()
{
    scanf("%d%d",&n,&m);
    if(n>m)swap(n,m);
    for(int d=n;d;d--)
    {
        f[d]=(ll)(n/d)*(m/d);//!!!下取整! 
        for(int k=2;k*d<=n;k++)f[d]-=f[k*d];
        ans+=f[d]*(d*2-1);
    }
    printf("%lld\n",ans);
    return 0;
}

2.推式子

  ∑ ∑ (gcd(i,j)*2-1) == ∑ ∑ ( ( ∑phi(d) )*2-1 ) == 2*∑ phi(d) ∑ ∑ - n*m == 2*∑ phi(d) * (n/d) * (m/d) - n*m。

  值得注意的是phi[1]应该视作等于1。因为小于等于1的和1gcd==1的数有1个。

  没错,d==1的时候就是把n和m都弄上了。考虑那个 d|i && d|j ,d==1就是出现在每个地方。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define ll long long
using namespace std;
const int N=1e5+5;
int n,m,phi[N],pri[N],cnt;
ll ans;
int main()
{
    scanf("%d%d",&n,&m);if(n>m)swap(n,m);
    for(int i=2;i<=n;i++)
    {
        if(!phi[i])pri[++cnt]=i,phi[i]=i-1;
        for(int j=1;j<=cnt&&i*pri[j]<=n;j++)
            if(i%pri[j]==0){phi[i*pri[j]]=phi[i]*pri[j];break;}
            else phi[i*pri[j]]=phi[i]*(pri[j]-1);
        ans+=(ll)phi[i]*2*(n/i)*(m/i);
    }
    ans+=2ll*n*m;
    printf("%lld\n",ans-(ll)n*m);
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/Narh/p/9388280.html