P1447能量採集
阿新 • • 發佈:2020-08-21
P1447能量採集
- 定義:(i,j)表示處於(i,j)的植物的貢獻
我們發現,點(i,j)與(0,0)的連線所過整點的數目為\(\gcd(i,j)\)
發現要是想記錄每個點的答案並不好算。那麼怎麼好算呢?
我們來找一找同一直線上的所有點答案的和的關係。先不考慮答案只考慮個數。發現,尋找一個點及其倍數的個數的和更加好算。而且,因為有n和m的限制,那麼向下取整的答案一定就是其本身。考慮容斥,我們只需要從大往小更新答案並將答案乘2減1加起來即可。
那麼對於一個點及其倍數的答案怎麼計算呢?
假設n小於m,那麼對於一個小於n的數i,顯然它的倍數的個數就是\((n/i)*(m/i)\),這樣一來我們只需要考慮小於n的所有數的個數就能夠統計n*m的所有數的答案了。至於為什麼\((m-n) * m\)
所以,答案即為
\[\displaystyle \sum_{i=1}^{n}num_i*(i*2-1) \]
其中\(\displaystyle num_i=(n/i)*(m/i)-\sum_{i=2}^{n/i}num_i\)
在程式碼中一個倒序迴圈即可,時間複雜度線性。
#include<iostream> #include<cstdio> #include<algorithm> #include<cctype> #include<cstring> #define int long long using namespace std; inline int read(){ int x=0,w=0;char c=getchar(); while(!isdigit(c))w|=c=='-',c=getchar(); while(isdigit(c))x=(x<<3)+(x<<1)+(c^48),c=getchar(); return w?-x:x; } const int maxn=1e5+10; int ans[maxn]; signed main(){ int n=read(),m=read(),Ans=0; if(n>m)swap(n,m); for(int i=n;i;i--){ ans[i]=(n/i)*(m/i); for(int j=2;j<=n/i;j++)ans[i]-=ans[i*j]; Ans+=(ans[i]*(i*2-1)); } printf("%lld",Ans); return 0; }