fireworks
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 positionx, then in next second one part will be in position x−1 and one inx+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
Hint
Author
题目大意
在一条长度不超过10^5的数轴上,有n个位置上有烟花若干,如果在某一时刻点燃烟花,那么烟花会在一秒的时间内分裂成两份并移动到相邻的左右两个位置,并且在下一秒,已经分裂的烟花会继续分裂,问在时间T时,位置w上有多少烟花。
问题分析
我们可以随意画一条数轴,模拟在位置x上的一个烟花的分裂过程
很容易就能看出烟花数量遵循杨辉三角的规律,由于题目数据上限10^5,使用递归计算杨辉三角肯定超时,所以我们考虑杨辉三角的一条性质:第m行第n个数即组合数C(m-1,n-1)。又题目要求计算结果取余10^9+7,在这里,我们使用费马小定理求乘法逆元,解决组合数的计算问题。
乘法逆元与组合数的计算
首先,什么是乘法逆元?
如果ax≡1(mod p),且gcd(a,p)=1(a,p互质),则称a关于1模p的乘法逆元是x。在维基百科中也称为倒数,我们可以理解为a关于mod p后的倒数的拓展。与倒数有相似的性质。
费马小定理
假如p是质数,且gcd(a,p)=1,那么 a (p-1)≡1(mod p)。(摘自百度百科)
现在,我们可以开始乘法逆元的计算。为什么费马小定理可以用来求乘法逆元呢?
将费马小定理的表达式变一下型,就简单多了,
a*a (p-2)≡1(mod p)
终于得到了乘法逆元,那么与组合数又有什么关系呢?
在题目中组合数的计算结果可能是非常大的,所以题目结果mod 10^9+7后输出,对于包含加减乘法运算的表达式,mod运算在表达式中的顺序不影响计算结果,但是对于除法,则不然。对于表达式中的除法运算,我们通常使用乘法逆元进行替换,将其转化为乘法运算。举个栗子,(a/b)%p => (a*b')%p => a%p*(b')%p(其中b'为b的乘法逆元)。(虽然,乘法逆元不完全等同于倒数,但,毕竟乘法逆元就是倒数的一种拓展)
组合数公式:
变换组合数公式得:
C(a,b) mod p
=A(a,b) / (b!) mod p
=( a! / (a-b)! ) / (b!) mod p
=( a! * ( (a-b)! )' ) * (b!)' mod p
最后一步,将乘法逆元代入上式,得
上式=a! * ( ( (a-b)! ) * (b!) ) ^ (p-2) mod p
AC代码
#include <cstdio>
#include <iostream>
#include <cmath>
using namespace std;
const int mod = 1e9+7;
long long int N[100005];//n!打表
void init()
{
N[0] = N[1] = 1;
for (int i=2; i<=100000; i++)
{
N[i] = (N[i-1]*i)%mod;
}
}
long long PowerMod(long long int a, long long int b, long long int c)
{
long long int ans = 1;
a = a%c;
while (b > 0)
{
if (b%2 == 1)
ans = (ans*a)%c;
b = b/2;
a = (a*a)%c;
}
return ans;
}
long long C(long long n, long long m)
{
return ((N[n]*PowerMod(N[n-m],mod-2, mod))%mod*PowerMod(N[m],mod-2, mod))%mod;
}
int main()
{
int n, T, w;
init();
while (cin >> n >> T >> w)
{
long long ans = 0;
for (int i=0; i<n; i++)
{
int x, c;//x:position;c:number
scanf ("%d %d",&x,&c);
int d = abs(w-x);
if (d<=T && (d+T)%2==0)//距离与时间同奇偶
{
ans = (ans+(c*C(T,(w-(x-T))/2))%mod)%mod;//(w-(x-T))/2:在杨辉三角中,x-T即时间终止时杨辉三角所在行的最前端烟花的所在位置,w减去最前端烟花的位置得到的就是w在杨辉三角中的位置,但是每个烟花中间都隔着一个烟花数量为0的位置,所以w在杨辉三角中的实际位置是(w-(x-T))/2,别忘了除以二
}
}
printf ("%lld\n",ans);
}
return 0;
}
补充
关于乘法逆元的计算,除了使用费马小定理外,还可以运用拓展欧几里得进行计算。
拓展欧几里得
已知整数a,b,拓展欧几里得算法可以在求得a,b的最大公约数的同时,找到整数x,y(其中一个可能是负数),是它们满足贝祖等式ax+by=gcd(a,b)。
对于乘法逆元的表达式,ax≡1(mod p),即ax+py=1(p为整数),刚好可以用拓展欧几里得算法计算x。注意对于一个数a,a关于b模p的乘法逆元x是唯一的,但是拓展欧几里得求得的x却是不唯一的,甚至有可能是一个负数,我们想要的乘法逆元应该满足0<x<p。
ac代码
#include <cstdio>
#include <iostream>
#include <cmath>
using namespace std;
const int mod = 1e9+7;
long long int N[100005];//n!打表
void init()
{
N[0] = N[1] = 1;
for (int i=2; i<=100000; i++)
{
N[i] = (N[i-1]*i)%mod;
}
}
long long gcd(long long x, long long y)
{
if(y == 0)
{
return x;
}
return gcd(y, x% y);
}
void exgcd(long long a, long long b, long long &x, long long &y)//long long &gcd
{
if(b == 0)
{
//gcd = a;
x= 1;
y= 0;
return;
}
exgcd(b, a% b, x, y);
long long t= x;
x= y;
y= t- a/ b* y;
return;
}
long long C(long long n, long long m)
{
long long x1, y1, x2, y2;//x1=N[n-m]';x2=N[m]'
exgcd(N[n-m], mod, x1, y1);
exgcd(N[m], mod, x2, y2);
x1=(x1+mod)%mod;
x2=(x2+mod)%mod;
return ((N[n]*x1)%mod*x2)%mod;
}
int main()
{
int n, T, w;
init();
while (cin >> n >> T >> w)
{
long long ans = 0;
for (int i=0; i<n; i++)
{
int x, c;//x:position;c:number
scanf ("%d %d",&x,&c);
int d = abs(w-x);
if (d<=T && (d+T)%2==0)//距离与时间同奇偶
{
ans = (ans+(c*C(T,(w-(x-T))/2))%mod)%mod;//(w-(x-T))/2:在杨辉三角中,x-T即时间终止时杨辉三角所在行的最前端烟花的所在位置,w减去最前端烟花的位置得到的就是w在杨辉三角中的位置,但是每个烟花中间都隔着一个烟花数量为0的位置,所以w在杨辉三角中的实际位置是(w-(x-T))/2,别忘了除以二
}
}
printf ("%lld\n",ans);
}
return 0;
}