1. 程式人生 > >●BZOJ 2005 NOI 2010 能量采集

●BZOJ 2005 NOI 2010 能量采集

str pen include stream return 微軟 b-s iostream com

題鏈:

http://www.lydsy.com/JudgeOnline/problem.php?id=2005

題解:

一個帶有容斥思想的遞推。
%%%
首先,對於一個點 (x,y) 在路徑 (0,0)->(x,y)上,經過的點數為 GCD(x,y)-1
所以改點的貢獻為 2*GCD(x,y)-1
N M
那麽,ANS = ∑ ∑(2*GCD(i,j)-1)
i=1 j=1
顯然超時。
考慮到 GCD<=100000,
那麽是否可以求出 f[i] 表示 GCD==i的點對 (x,y)有多少個。
然後用f[i]去得出答案 (ans+=f[i]*(2*i-1))?

接下來就是神奇的遞推了。
f[i]=(N/i)*(M/i) - f[i*k] (i*k<=min(N,M))
上式中 (N/i)*(M/i) 求得的是 有i這個公約數的點對(x,y)的個數
因為這些點對的最大公約數GCD可能為 i,2i,3i......
所以減掉f[2i],f[3i],f[4i]......就得到了f[i].

代碼:

#include<cstdio>
#include<cstring>
#include<iostream>
#define ll long long
using namespace std;
ll f[100005];
ll N,M,K,ans;
int main()
{
	freopen("energy.in","r",stdin);
	freopen("energy.out","w",stdout);
	cin>>N>>M; K=min(N,M);
	for(int i=K;i>=1;i--){
		f[i]=(N/i)*(M/i);
		for(int j=2;i*j<=K;j++)
			f[i]-=f[i*j];
		ans+=f[i]*(2*i-1);
	}
	printf("%lld",ans);
	return 0;
}

●BZOJ 2005 NOI 2010 能量采集