BZOJ 3260. 跳

传送门

首先肯定要确定贪心走法,然后再考虑代价

首先注意到 $(x,y)$ 位置的值其实就是 $C(x+y,x)$ 的值

那么如果要从 $(0,0)$ 到 $(n,m)$,我们肯定不会往回走(不会跑出 $(n,m)$ 的矩形再绕回来)

归纳一下我们只要考虑往上和往右

不妨设 $m>n$

注意到边缘的代价比中间小得多,容易想到先绕着边缘走到 $(0,m)$,然后再直接往 $(n,m)$ 走

证明也可以考虑归纳法,首先显然到达 $(0,1)$,$(1,0)$ 我们来的方向是值比较小的方向,然后归纳得到从 $(n,m)$ 往回走最优路径就是每次都往值较小的位置走

所以配合一下自己画的图就证明了贪心是正确的

然后计算代价,走到 $(0,m-1)$ 代价都是 $1$ 很好计算

然后就是从 $(0,m)$ 一路往右走到 $(n,m)$,代价为 $\sum_{k=0}^{n}\binom{m+k}{k}$

展开一下就是 $\sum_{k=0}^{n} \frac {(m+k)!} {m!k!}$ 注意到 $((m+k)!)/(m!)$ 可以除掉,就变成了 $\prod_{k=m+1}^{m+k}k$

所以就是 $\sum_{k=0}^{n} \frac {\prod_{k=m+1}^{m+k}k} {k!}$

然后我看着这个式子愣了一下,好像没法再化了

突然发现题目保证 $nm<=10^{12}$,又因为 $n<m$ 了,所以 $n<=10^6$

然后动态维护一下当前 $\prod_{k=m+1}^{m+k}k$ 和 $k!$ 就做完了,记得模意义下要逆元,最后答案要加上走边缘的 $m$,别像我一样被胜利冲昏头脑...

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
using namespace std;
typedef long long ll;
inline ll read()
{
    ll x=0,f=1; char ch=getchar();
    while(ch<'0'||ch>'9') { if(ch=='-') f=-1; ch=getchar(); }
    while(ch>='0'&&ch<='9') { x=(x<<1)+(x<<3)+(ch^48); ch=getchar(); }
    return x*f;
}
const ll mo=1e9+7;
ll n,m,ans;
ll ksm(ll x,ll y)
{
    ll res=1;
    while(y)
    {
        if(y&1) res=res*x%mo;
        x=x*x%mo; y>>=1;
    }
    return res;
}
int main()
{
    n=read(),m=read();
    if(n>m) swap(n,m); ll sum1=1,sum2=1,now1=m%mo,now2=1;
    for(int i=0;i<=n;i++)
    {
        ans=((ans+sum1*ksm(sum2,mo-2)%mo)%mo+mo)%mo;
        now1++; now1%=mo; sum1=sum1*now1%mo;
        sum2=sum2*now2%mo; now2++;
    }
    printf("%lld\n",(ans+m)%mo);
}

 

posted @ 2019-09-18 08:39  LLTYYC  阅读(153)  评论(0编辑  收藏  举报