1. 程式人生 > >NOI2010能量采集(數學)

NOI2010能量采集(數學)

過程 namespace spa mat turn 機器 cst style col

棟棟有一塊長方形的地,他在地上種了一種能量植物,這種植物可以采集太陽光的能量。在這些植物采集能量後,棟棟再使用一個能量匯集機器把這些植物采集到的能量匯集到一起。

棟棟的植物種得非常整齊,一共有n列,每列有m棵,植物的橫豎間距都一樣,因此對於每一棵植物,棟棟可以用一個坐標(x, y)來表示,其中x的範圍是1至n,表示是在第x列,y的範圍是1至m,表示是在第x列的第y棵。

由於能量匯集機器較大,不便移動,棟棟將它放在了一個角上,坐標正好是(0, 0)。

能量匯集機器在匯集的過程中有一定的能量損失。如果一棵植物與能量匯集機器連接而成的線段上有k棵植物,則能 量的損失為2k + 1。例如,當能量匯集機器收集坐標為(2, 4)的植物時,由於連接線段上存在一棵植物(1, 2),會產生3的能量損失。註意,如果一棵植物與能量匯集機器連接的線段上沒有植物,則能量損失為1。現在要計算總的能量損失。

下面給出了一個能量采集的例子,其中n = 5,m = 4,一共有20棵植物,在每棵植物上標明了能量匯集機器收集它的能量時產生的能量損失。

題意為求1-n之間的所有數和1-m之間的所有數兩兩之間的GCD。
一道非常經典的莫比烏斯反演的例題,但有一種容斥的方法更加簡單。

考慮枚舉每個gcd,那麽gcd為當前gcd的倍數的數對就有n/gcd*m/gcd個。

在考慮把多余的方案去掉,只要枚舉gcd的所有倍數,把它們都減掉就好了。

做的時候就倒著枚舉gcd就可以了。

#include<iostream>
#include<cstdio>
using
namespace std; int gcd(int x,int y){return y?gcd(y,x%y):x;} int n,m,mi; long long ans,f[100009]; int main() { cin>>n>>m;mi=min(m,n); for(int i=mi;i>=1;--i) { f[i]=(long long)(m/i)*(n/i); for(int j=(i<<1);j<=mi;j+=i)f[i]-=f[j]; ans+=f[i]*(i*2
-1); } cout<<ans; return 0; }

NOI2010能量采集(數學)