ACM-ICPC 2018 南京赛区网络预赛——J.Sum

  • 原题链接:https://nanti.jisuanke.com/t/30999
  • 题意:对于一个正整数n,f(n)表示有几个这样的等式 n = a*b,其中a和b都不能包含平方数,即a和b中的质因子数不能出现超过一次。其中 n = a*b 和n = b*a算作两个(a ≠ b). 现求 f(1)+……+f(n)。
  • 思路 :此题需要以下两个知识点作为知识前提 (当然你可能没看过定理但是懂得基本含义):

1、算数基本定理 :每个大于1的自然数均可写为质数的积,而且这些素因子按大小排列之后,写法仅有一种方式。 即 x=p1^k1*p2^k2*p3^k3…pn^kn。
2、 欧拉筛法(也即线性筛法),可以参考博客
https://blog.csdn.net/qq_39763472/article/details/82428602

通过第一个定理,可以将问题转化成对n的素因子个数的讨论;而通过讨论素因子,便需要用到欧拉筛法,毕竟本题2e7的数据,也只有线性筛hold得住。
可以分以下情况讨论
1)若n是素数,则f(n) = 2
2) 若n的某个质因子个数超过2,则f(n) = 0。因为 n = a*b,若某个质因子个数超过2,那么a和b中必有一个它的平方
3) 若n的某个质因子个数为2,则f(n) = f(n/这个质因子的平方)。因为若使a和b中的此质因子个数都不为2,则a和b中各自有这个质因子,相当于贡献为0,则从n中消去此项即可。
4)若gcd(a,b) = 1,则 f(n) = f(a) * f(b).

  • 代码 :
#include "bits/stdc++.h"
using namespace std;
#define mem(a,b) memset(a,b,sizeof(a))
#define fori(i,l,u) for(int i = l;i < u;i++)
#define forj(j,l,u) for(int j = l;j < u;j++)
#define pb push_back
#define mk make_pair
#define F first
#define S second
typedef long long  ll;
typedef pair<int, int> pi;
typedef pair<string,int> ps;
typedef vector<int> vi;
typedef vector<ll> vl;
typedef vector<pi> vpi;
const int maxn = 2e7+ 5;
int f[maxn];
int n;
int prime[maxn];
int visit[maxn];
void init(){        //线性筛法
    mem(visit,0);
    mem(prime,0);
    f[1] = 1;
    for (int i = 2; i < maxn; i++) {
        if (!visit[i]) {            // i是素数的情况
            prime[++prime[0]] = i;  //纪录这个素数,其中prime[0]相当于cnt = 0,cnt++
            f[i] = 2;               //如果i 是素数,则f[i] = 2
        }
        for (int j = 1; j <= prime[0] && i*prime[j] < maxn; j++) {
            int ans = i*prime[j];
            visit[ans] = 1;      // i*prime[j]是合数
            if (i%prime[j] != 0) {      //如果 gcd(i,prime[j]) == 1,那么 f(i*prime[j]) = f(i)*f(prime[j])
                f[ans] = f[i] * f[prime[j]];
            }
            else{                       //如果 prime[j]是i的质因子
                if (i %(prime[j]*prime[j]) == 0) {  //如果 ans 中prime[j]这个质因子个数超过2,则f[ans] = 0;
                    f[ans] = 0;
                }
                else{       //如果ans中有两个prime[j],则 结果为 f[ans/(prime[j]*prime[j])] 即 f[i]/2
                    f[ans] = f[ans/(prime[j]*prime[j])];
                }
                break;
            }
        }
    }
    for (int i = 1; i < maxn; i++) {  //求前缀和
        f[i] += f[i-1];
    }
}
void solve(){
    init();
    int t;
    cin>>t;
    while (t--) {
        int n;
        cin>>n;
        cout<<f[n]<<endl;
    }
}
int main()
{
    solve();
    return 0;
}


可以和欧拉筛法比较一下,就是在其框架上加上了对ans的质因子个数分情况讨论。本题的核心便是对n质因子个数分情况讨论。

猜你喜欢

转载自blog.csdn.net/qq_39763472/article/details/82424965
今日推荐