题解 CF1146D Frog Jumping

题目链接

我们设(h(i)),表示要到达点(i),最远必须经过(y)。特别地,如果永远无法到达(i),令(h(x)=inf)。那么,每个位置(i),会在( ext{lim}in[h(i),m])时成为一个能够到达的位置(其中( ext{lim})表示可以走的范围为([0, ext{lim}]))。因此最终的答案就是(sum_{i=0}^{m}max(0,m-h(i)+1))


显然,点(i)能够在有限步之内到达((h(i) eq inf))的一个必要条件是,方程(ax+by=i)有整数解。根据裴蜀定理,该条件等价于(i)(gcd(a,b))的倍数。不难猜到,当(i)比较大时,如果(i)(gcd(a,b))的倍数,那么(h(i)=i)。因为我可以在走的过程中(例如需要走(x)步向前的,(y)步向后的),那么只要当前坐标(geq b),且向后退的(y)步没用完,我就先往回退。根据这个策略,不难发现,在用完所有往回退的步数之前,我使用的坐标范围不会超过([0,a+b])。这也就对应了前面我们猜想中的“(i)比较大”,其实就是指(i>a+b)时:

[egin{cases} h(i)=i&&gcd(a,b)|i\ h(i)=inf&& ext{otherwise} end{cases} ]

因此,对于(i>a+b)的部分,对答案的贡献就是:

[egin{align} &sum_{i=a+b+1}^{m}[gcd(a,b)|i](m-i+1)\ =&sum_{i=lceilfrac{a+b+1}{gcd(a,b)} ceil}^{lfloorfrac{m}{gcd(a,b)} floor}(m-gcd(a,b)cdot i+1) end{align} ]

可以用等差数列求和的公式(O(1))计算这部分的贡献。


(i<a+b)的时候,不一定能够在(a+b)之间自由挪动,所以并不能确定(h(i))具体等于多少。但是,如果确定了某个(h(j)),则可以用(h(j))来更新(h(j-b))(h(j+a))的值。这很像求最短路时的“松弛”操作。所以可以用dijkstra来实现这一过程,暴力算出前(a+b)(h)值。起点是(h(0)=0)。时间复杂度(O((a+b)log (a+b)))

参考代码:

//problem:CF1146D
#include <bits/stdc++.h>
using namespace std;

#define pb push_back
#define mk make_pair
#define lob lower_bound
#define upb upper_bound
#define fi first
#define se second
#define SZ(x) ((int)(x).size())

typedef unsigned int uint;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int,int> pii;

const int MAXN=2e5,INF=0x3f3f3f3f;
int m,a,b,h[MAXN+5];
int gcd(int x,int y){return !y?x:gcd(y,x%y);}

int main() {
	cin>>m>>a>>b;
	memset(h,0x3f,sizeof(h));
	priority_queue<pii>q;
	h[0]=0;
	q.push(mk(0,0));
	while(!q.empty()){
		pii cur=q.top();
		q.pop();
		if(-cur.fi>h[cur.se])continue;
		
		if(cur.se>b&&h[cur.se-b]>h[cur.se]){
			h[cur.se-b]=h[cur.se];
			q.push(mk(-h[cur.se-b],cur.se-b));
		}
		if(cur.se<=b&&h[cur.se+a]>max(h[cur.se],cur.se+a)){
			h[cur.se+a]=max(h[cur.se],cur.se+a);
			q.push(mk(-h[cur.se+a],cur.se+a));
		}
	}
	ll ans=0;
	for(int i=0;i<=min(m,a+b);++i){
		ans+=max(0,m-h[i]+1);
	}
	if(m>a+b){
		ll g=gcd(a,b);
		ll lo=(a+b+1)/g+((a+b+1)%g!=0);
		ll hi=m/g;
		if(lo<=hi){
			ans+=(hi-lo+1)*(m+1)-(lo*g+hi*g)*(hi-lo+1)/2;
		}
	}
	cout<<ans<<endl;
	return 0;
}
原文地址:https://www.cnblogs.com/dysyn1314/p/12775622.html