您的位置:首页 > 其它

【NOI2015模拟8.17】最短路(shortest)//2018.2.5

2018-02-06 20:51 197 查看

题目

Description

小Y最近学得了最短路算法,一直想找个机会好好练习一下。话虽这么说,OJ上最短路的题目都被他刷光了。正巧他的好朋友小A正在研究一类奇怪的图,他也想凑上去求下它的最短路。

小A研究的图可以这么看:在一个二维平面上有任意点(x,y)(0<=x<=N,0<=y<=M,且x,y均为整数),且(x,y)向(x-1,y)(必须满足1<=x)和(x,y-1)(必须满足1<=y)连一条边权为0的双向边。

每个点都有一个非负点权,不妨设(x,y)的权值为F[x][y],则有:

1.x=0或y=0:F[x][y]=1;2.其他情况:F[x][y]=F[x-1][y]+F[x][y-1]。

现在,小Y想知道(0,0)到(N,M)的最短路,即使得经过的点的权值之和最小。为了炫耀自己学过最短路算法,他决定和你进行一场比赛,看谁的程序跑得快。然则小Y没有学过高精度算法,所以他希望输出答案时只输出答案模1000000007后的值。

Input

一行两个正整数N,M,表示图的大小。

Output

一行一个整数Ans,表示答案模1000000007后的值。

Hint

10%的数据满足N,M<=20;

30%的数据满足N,M<=100;

60%的数据满足min(N,M)<=100;

100%的数据满足N*M<=10^12。

解题思路

这个图就是个划分后的矩形,也就是网格图。

20分看这里:暴力求出每个点权,然后使用DP或最短路算法。总效率O(NM)或O(NMLog(NM))。答案的范围在Int64/long long范围内。

60分看这里:暴力求出每个点权,然后使用DP或最短路算法。使用高精度储存答案。总效率O(NMA)或O(NMALog(NM))。其中A为答案长度。

观察原图,将x+y值相同的点(x,y)归为一层,将层按x+y值从小到大看,会发现其实整个图是杨辉三角的一部分。

![这里写图片描述](https://img-blog.csdn.net/20180206194840569?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvcXFfMzk4OTc4Njc=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)


容易看出,每层权值最小的点或都在最左侧或都在最右侧。由于N,M互换并不影响答案,所以假如每层权值最小的点都在最右侧,则交换N,M,保证每层权值最小的点都在最左侧。

从本质上说,就是该图的最短路是确定的,都是沿着矩形外围一圈走,且一开始先走矩形长的一边。

保证N<=M,那么答案可以直接算出,也就是M+C[M][0]+C[M+1][1]+…+C[M+N]

120分看这里:用一个矩阵维护C[K][0]-C[K]
的值。通过快速幂可以迅速得出C[M][0]-C[M]
的值,由于答案涉及的排列数只有N+1项,所以接着逐级递推即可。总效率O(N^3LogM)。

200分看这里:把后面的排列数分别拆解开看:

C[M][0]=1;
C[M+1][1]=(M+1)/1;
C[M+2][2]=(M+1)*(M+2)/(1*2)=C[M+1][1]*((M+2)/2);
C[M+3][3]=C[M+2][2]*((M+3)/3);
…
C[M+N]
=C[M+N-1][N-1]*((M+N)/N)。


由于模的数是一个素数,所以除以数a直接转化成乘数a的逆元。接着直接递推即可。总效率O(NLogN)。

【再详细一点吧】其实就是费马小定理……

假如p是质数,且gcd(a,p)=1,那么 a(p-1)≡1(mod p),例如:假如a是整数,p是质数,则a,p显然互质(即两者只有一个公约数1),那么我们可以得到费马小定理的一个特例,即当p为质数时候, a^(p-1)≡1(mod p)。实际上,它是欧拉定理的一个特殊情况(即

可能会发现跟题目没有什么区别,但是题目其实就是一个杨辉三角形,让我们求1到C(n,m)的….其实也就是求C(n,n+m),所以用逆元,而逆元要用到费马小定理,而x的y次方又要用到快速幂,所以就是正解了;

代码

#include<iostream>
#include<cstdio>
#include<cmath>
using namespace std;
const int inf=1000000007;
long long x=1,y=1,n,m;

long long sst(long long b,long long a)
{
long long ans=1;
while (a)
{
if (a&1) ans=(ans*b)%inf;
a>>=1;
b=(b*b)%inf;
}
return ans;
}

int main()
{
scanf("%lld%lld",&n,&m);
long long t=0;
if (n<m) { t=n;n=m;m=t;}
for (long long i=n+2;i<=n+m+1;i++)
x=(x*i)%inf; //x的阶乘
for (long long i=1;i<=m;i++)
y=(y*i)%inf;//y的阶乘
printf("%lld",(x*sst(y,inf-2)%inf+n)%inf);  //逆元
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  数论