luogup3708 koishi的數學題
看題,嗯很好。
暴力思路清晰,直接模擬流程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(intmaxx) { 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的數學題