山东省第八届ACM省赛C题

本文介绍了一种模拟烟花爆炸过程的算法。通过分析烟花在不同时间点的状态,利用组合数学原理和快速幂运算,解决了求特定位置烟花数量的问题。文章详细阐述了如何使用乘法逆元进行组合数计算的方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值