1. 程式人生 > >luogup3708 koishi的數學題

luogup3708 koishi的數學題

數學 基本 註意 很好 到手 一道 這不 PE div

看題,嗯很好。

暴力思路清晰,直接模擬流程20分到手;

再仔細看,好像是一道數學題,思路不太明顯。那我們打個表看一下;

那我們就用樣例來說明一下

n=10;
f[5]=5%1+5%2+5%3+5%4+5%5+5%6+5%7+5%8+5%9+5%10;
0 1 2 1 0 5 5 5 5 5
f[6]=6%1+6%2+6%3+6%4+6%5+6%6+6%7+6%8+6%9+6%10;
0 0 0 2 1 0 6 6 6 6
我們發現f[5]->f[6]是f[5]裏每一個數都加1然後再減一些東西得到的;
那我們嘗試的加一下;
f[5]=5%1+5%2+5%3+5%4+5%5+5%6+5%7+5%8+5%9+5%10;
0 1 2 1 0 5 5 5 5 5
1 2 3 2 1 6 6 6 6 6
比較一下和f[6]的區別,發現少了1 2 3 6四個數,等等,這不是六的正因子之和嗎。
那那那f[6]->f[7]呢.,我們發現少的同樣是7的正因子之和;
那我們可以猜測一下f[i]的遞推式了;
  f[i]=f[i-1]+n-(i的正因子之和);
那考慮證明一下這個式子;
1.相鄰兩個數(x,y)互質,那麽除了1之外,不可能有x%k==0&&y%k==0;(y=x+1)
如果能使x+1%k==0,那麽x%k==x;
2.感性理解一下,我們再算f[i]的時候,有可能f[i-1]+1然後%一下變成0了,而我們沒有處理這部分,
想象一下x%i=0,那麽i是啥,當然是x的因數了,而且包括1;
是不是很開心,我們可以0(n)遞推求出答案了,但還有一個問題,i的正因子之和怎麽求啊;
1.nlogn的算法 

 num[1]=1;

   for(int i=2;i<=n;i++)

        num[i]=i+1;
    for(int i=2;i*i<=n;i++)
        for(int j=i;j<=n/i;j++)
            if(i==j)
                num[i*j]+=i;
            else
               num[i*j]+=(i+j);

2.因為正因數之和這個函數是積性函數,所以可以用線性篩篩出來(至於如何證這個是積性函數,我就不太會了,不過好像可以用算數基本定理)

void ERS(int
maxx) { for(int i=2;i<=maxx;i++) { if(ipri[i]) { pri[++cnt]=i; spri[i]=i+1;//質數的因子和子是自己+1 facs[i]=i;//質數的最大因子是自己 } for(int j=1;j<=cnt&&i*pri[j]<=maxx;j++) { int noww=i*pri[j]; ipri[noww]
=false; facs[noww]=pri[j];//更新 if(!(i%pri[j]))//發現i是枚舉到的質數的倍數,準備跳車 { facs[noww]=facs[i]*pri[j];//積性函數性質,更新 if(facs[noww]==noww)//最大因子就是自己說明這個數的facs已經更新完了,該更新spri了 for(int k=1;k<=noww;k*=pri[j]) spri[noww]+=k;//因為i是枚舉到的質數的倍數,用加法更新即可 else spri[noww]=spri[facs[noww]]*spri[noww/facs[noww]];//註意這裏更新,下面就break掉了 break; } spri[noww]=spri[pri[j]]*spri[i];//積性函數性質,更新 } } } }

求出正因數之和,那麽這道題不就非常愉悅的A掉了

技術分享圖片
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
long long num[1200000],n,f[1200000];
int main()
{
    scanf("%lld",&n);
        num[1]=1;
    for(int i=2;i<=n;i++)
        num[i]=i+1;
    for(int i=2;i*i<=n;i++)
        for(int j=i;j<=n/i;j++)
            if(i==j)
                num[i*j]+=i;
            else
               num[i*j]+=(i+j);
    f[1]=n-1;
    for(register int i=2;i<=n;i++)
        f[i]=f[i-1]+n-num[i];
    for(register int i=1;i<=n;i++)
        printf("%lld ",f[i]);    
    return 0;
}
由於我不知道為啥是積性函數,所以就用了第一種(標程)

luogup3708 koishi的數學題