zoukankan      html  css  js  c++  java
  • 题解 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;
    }
    
  • 相关阅读:
    适配器模式
    事务
    JUnit4
    命令模式
    组合模式
    MySQL语法
    MyEclipse工具
    数据源和连接池
    Annotation
    Java反射机制
  • 原文地址:https://www.cnblogs.com/dysyn1314/p/12775622.html
Copyright © 2011-2022 走看看