Problem Description
Hmz likes to play fireworks, especially when they are put regularly.
Now he puts some fireworks in a line. This time he put a trigger on each firework. With that trigger, each firework will explode and split into two parts per second, which means if a firework is currently in position x, then in next second one part will be in position x−1 and one in x+1. They can continue spliting without limits, as Hmz likes.
Now there are n fireworks on the number axis. Hmz wants to know after T seconds, how many fireworks are there in position w?
Input
Input contains multiple test cases.
For each test case:
The first line contains 3 integers n,T,w(n,T,|w|≤10^5)
In next n lines, each line contains two integers xi and ci, indicating there are ci fireworks in position xi at the beginning(ci,|xi|≤10^5).
Output
For each test case, you should output the answer MOD 1000000007.
Example Input
1 2 0
2 2
2 2 2
0 3
1 2
Example Output
2
3
题意
烟花在每秒都会分裂一次,并且分裂成的两半刚好落在相邻的两点,然后它们也可以继续分裂。
给出 n 个点烟花的初始数量,问经过 T 秒后在点 w 有多少数量的烟花。
思路
考虑所有的烟花分裂都是一样的,并且其分裂之后所形成的局势仅和时间有关。
所以我们只需要关心一个烟花是如何分裂的:
0 0 0 0 0 1 0 0 0 0 0
0 0 0 0 1 0 1 0 0 0 0
0 0 0 1 0 2 0 1 0 0 0
0 0 1 0 3 0 3 0 1 0 0
0 1 0 4 0 6 0 4 0 1 0
第几行代表第几秒,行中的每个数字代表当前时间该点烟花的数量,于是我们发现了一个中间插入了 0 的杨辉三角,计算方法也就是组合数咯~
从上面的矩阵我们可以发现,当时间与距离同奇偶的时候才处于杨辉三角中,其余情况都为 0 。
因为题目中数据比较大,所以计算组合数需要用到乘法逆元,先打表求出 n! ,然后根据组合数公式计算即可。
参考公式:
在开始之前我们先介绍2个定理:
1.乘法逆元(在维基百科中也叫倒数,当然是 mod p后的,其实就是倒数不是吗?):
如果ax≡1 (mod p),且gcd(a,p)=1(a与p互质),则称a关于模p的乘法逆元为x。
2.费马小定理(定义来自维基百科):
假如a是一个整数,p是一个质数,那么是p的倍数,可以表示为
如果a不是p的倍数,这个定理也可以写成
好了,在明白上面的定理后我们开始分析乘法逆元:ax≡1 (mod p) 这个等式用中文描述就是 a乘一个数x并
模p等于1,即 a%p*x%p=res,res%p=1;看上去就是同余定理的一个简单等式- -。那么问题来了。
为什么可以用费马小定理来求逆元呢?
由费马小定理 ap-1≡1 , 变形得 a*ap-2≡1(mod p),答案已经很明显了:若a,p互质,因为a*ap-2≡1(mod p)且
a*x≡1(mod p),则x=ap-2(mod p),用快速幂可快速求之。
知道逆元怎么算之后,那么乘法逆元有什么用呢?
做题时如果结果过大一般都会让你模一个数,确保结果不是很大,而这个数一般是1e9+7,而且这个数又是
个素数,加减乘与模运算的顺序交换不会影响结果,但是除法不行。有的题目要求结果mod一个大质数,如
果原本的结果中有除法,比如除以a,那就可以乘以a的逆元替代。(除一个数等于乘它的倒数,虽然这里
的逆元不完全是倒数,但可以这么理解,毕竟乘法逆元就是倒数的扩展)。
重点
所以利用逆元和组合数公式(一般要求mod p为质数) 。
组合数公式可以进一步优化( PS : inv( x )表示 x 的逆元 )
C(a,b)
= ( a! / ( a - b )! ) / ( b! ) mod p
= a! * inv ( b! ) * inv ( ( a - b )! ) mod p
= a! * ( b! * ( a - b ) ! ) ^ ( p - 2 ) mod p
AC代码带注释:
#include<cstdio>
#include<cmath>
#define mod 1000000007
using namespace std;
long long N[200000]; ///用来装n!
long long mult(long long a) ///此函数为快速幂,用来求a的逆元
{
long long b=mod-2;
long long ans = 1;
a = a % mod;
while(b>0)
{
if(b%2==1)
ans = (ans * a) % mod;
b = b/2;
a = (a * a) % mod;
}
return ans;
}
long long Cmn(long long n,long long m) ///此函数用来求C(n,m)
{
long long ret;
ret=(N[n]%mod*mult(N[m])%mod*mult(N[n-m])%mod)%mod; ///这个公式就是用逆元和组合数公式优化后的公式
return ret;
}
int main()
{
N[1]=N[0]=1;
for(long long i=2; i<100000+5; i++) ///初始化N[];先打出表备用
{
N[i]=(i*N[i-1])%mod;
}
long long n,t,w;
while(scanf("%lld %lld %lld",&n,&t,&w)!=EOF)
{
long long sum=0;
while(n--)
{
long long xi,ci;
long long k;
long long c;
scanf("%lld %lld",&xi,&ci);
k=abs(w-xi);
if((k+t)%2==0&&k<=t) ///保证位置与时间同奇同偶且最终位置处于爆炸范围内
{
c=Cmn(t,(w-(xi-t))/2)%mod;
sum+=c*ci%mod;
}
else
{
sum+=0%mod;
}
}
printf("%lld\n",sum%mod);
}
return 0;
}