CQYZ_Vijos_P3650 杨辉三角形

【问题描述】

  小沐在在奥数课上刚学习了组合数C(n,m),以及与组合数相关的一个最重要内容——杨辉三角形。如下图所示的是一个7行的杨辉三角形。
杨辉三角形图示
  小沐把杨辉三角形从左到右的每列依次编号为0,1,2,……,从上到下的每行编号为0,1,2,……。那么杨辉三角形的第i行第j列的数就是C(i,j)。
  现在小沐在思考这样的问题:给出任意n,i,j,那么在n行的杨辉三角形中第i行与第j列的和分别是多少?由于小沐还小,需要你来帮助他解决这个问题。

【输入格式】

  第一行是整数T,表示数据组数,
  接下来的T行,每行包含3个整数:n ,i ,j ( i, j < n),表示询问n行的杨辉三角形中第i行的和与第j列的和。

【输出格式】

  输出有T行,每行输出两个整数,分别为对应输入的n行杨辉三角形的第i行的和以及第j列的和,他们可能很大,请模1004535809后输出。

【输入样例】

3
7 1 0
5 4 3
6 3 2

【输出样例】

2 7
16 5
8 20

【数据范围】

对于50%的数据, 1 <= n <= 5000。
对于100%的数据,T<=100,1<=n<=1000000。

题解:

  简单(?)的数论题
  一问:杨辉三角形的第i行的和
    ans1=2^i
  证明一:
    很明显第i行的和是第i-1行的和的2倍,且第0行(注意编号从0开始)的和为1
    具体说一说:
    1. 设d[i][j]为杨辉三角形第i行第j列的数,sum[i]为杨辉三角形第i行的和,则
      sum[i]=d[i][0]+d[i][1]+…+d[i][i];
    2.在杨辉三角形中,有d[i][j]=d[i-1][j-1]+d[i-1][j],且d[i][0]=d[i][i]=1,所以
      sum[i]=d[i][0]+d[i][1]+…+d[i][i]
         =d[i-1][0]+(d[i-1][0]+d[i-1][1])+…+(d[i-1][i-2]+d[i-1][i-1])+d[i-1][i-1]
         =2*(d[i-1][0]+d[i-1][1]+…+d[i-1][i-2]+d[i-1][i-1]
         =2*sum[i-1]
  证明二:
    二次项定理 (x+y)^n=C(n,0)* x^n+C(n,1)* (x^(n-1))*y+…+C(n,n)*y^n
    当x=y=1时,有
      2^n=C(n,0)+C(n,1)+…+C(n,n)
    也就是杨辉三角形第n行的和

  二问:杨辉三角形的第j列的和
    ans2=C(n,j+1) (注意n是杨辉三角形的行数,j是编号)
  证明:
    在杨辉三角形中,有d[i][j]=d[i-1][j-1]+d[i-1][j],且d[i][i]=d[i+1][i+1]=1,所以
       d[j][j]+d[j+1][j]+…+d[n-1][j]  (再说一次编号是0~n-1)
      =d[j+1][j+1]+d[j+1][j]+…+d[n-1][j]
      =d[j+2][j+1]+d[j+2][j]+…+d[n-1][j]
      =d[j+3][j+1]+d[j+3][j]+…+d[n-1][j]
      ……
      =d[n-1][j+1]+d[n-1][j]
      =d[n][j+1]
  数据规模达到100000,打表不可能了,只有用组合公式算
  最后代码实现的时候由于要取模,且公式里有除法,有两种处理方式:
    1.分子分母分解质因数,变成形如 a1^q1*a2^q2*…*an^qn 的形式,用指数相减的方式来计算
    2.乘法逆元
  其中方法1可行的原因是组合数一定为整数,如果除不尽是不能这样处理的
  方法2一定要先打一个阶乘的表出来,否则有可能超时

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cmath>
#include<cstring>
#include<cstdlib>
using namespace std;
typedef long long ll;
const int maxn=1000005,mo=1004535809;
ll jie[maxn]={0};
int n,I,J;
int in(){
    int x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-') f=-f;ch=getchar();}
    while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
    return x*f;
}
ll mypow(int a,int q){
    ll ret=1,tmp=a;
    while(q){
        if(q&1) ret=ret*tmp%mo;
        q/=2,tmp=tmp*tmp%mo;
    }
    return ret;
}
int main(){
    int T=in();
    ll ans1,ans2,tmp1,tmp2,tmp3;
    jie[0]=jie[1]=1;
    for(int i=2;i<=1000001;i++) jie[i]=jie[i-1]*i%mo;
    while(T--){
        n=in(),I=in(),J=in();
        ans1=mypow(2,I);
        tmp1=jie[J+1],tmp2=jie[n],tmp3=jie[n-J-1];
        tmp1=mypow(tmp1,mo-2),tmp3=mypow(tmp3,mo-2);
        ans2=tmp2*tmp3%mo*tmp1%mo;
        printf("%lld %lld\n",ans1,ans2);
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值