HDU5226 Tom and matrix(BestCoder Round #40)(lucas定理)

11 篇文章 0 订阅

Tom and matrix

 
 Accepts: 29
 
 Submissions: 225
 Time Limit: 3000/1500 MS (Java/Others)
 
 Memory Limit: 65536/65536 K (Java/Others)
问题描述
Tom放学回家的路上,看到天空中出现一个矩阵。Tom发现,如果矩阵的行、列从0开始标号,第i行第j列的数记为
   
   
    
    ai,j
   
   ,那么
   
   
    
    ai,j=Cji
   
   
如果i < j,那么
   
   
    
    ai,j
   
   =0
Tom突发奇想,想求一个矩形范围内所有数的和。Tom急着回家,当然不会自己算,所以就把任务交给你了。
因为数可能很大,答案对一个质数p取模。
输入描述
输入包含多组数据(大约8组)。每组数据只有一行五个非负整数,
   
   
    
    x1y1x2y2p
   
   ,你要求的是
   
   
    
    x2i=x1y2j=y1ai,j
   
   模p后的值。

   
   
    
    x1x2105,y1y2105,2p109
   
   
输出描述
对于每组数据输出一行,答案模p。
输入样例
0 0 1 1 7
1 1 2 2 13
1 0 2 1 2
输出样例
3
4
1
大神的代码写的好简洁
Problem C
因为

     
     
      
      bi=aCki=Ck+1b+1Ck+1a
     
     
所以求同一列的数的和可以变成求两个组合数的差。
因为要模p,p还可能很小,所以可以用lucas定理:

     
     
      
      CmnCm/pn/pCm%pn%p(mod p)
     
     
可以预处理阶乘。
设
     
     
      
      n=max(x1,y1,x2,y2)
     
     ,那么
时间复杂度:O(
     
     
      
      nlogn
     
     

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<algorithm>
#define maxn 200010
using namespace std;
typedef long long LL;
typedef unsigned long long ULL;
int fact[maxn],inv[maxn],mod;
int Power(int p,int n){
    int ans=1;
    for(;n;n>>=1,p=(LL)p*p%mod)
        if(n&1)
        ans=(LL)ans*p%mod;
    return ans;
}
void Prepare(int n){
    fact[0]=1,inv[0]=1;
    for(int i=1;i<=n&&i<mod;++i)
        fact[i]=(LL)fact[i-1]*i%mod,inv[i]=Power(fact[i],mod-2);
}
int C(int n,int m){
    if(n<m) return 0;
    return (LL)fact[n]*inv[m]%mod*inv[n-m]%mod;
}
int Lucas(int n,int m){
    if(n<mod&&m<mod)
        return C(n,m);
    return (LL)Lucas(n/mod,m/mod)*Lucas(n%mod,m%mod)%mod;
}
int work(int lx,int ly,int rx,int ry){
    LL ans=0,now=0;
    for(int i=ly;i<=ry;++i)
        now=(now+Lucas(lx,i))%mod;
    ans=now;
    for(int i=lx+1;i<=rx;++i)
        now=(now*2-Lucas(i-1,ly)-Lucas(i-1,ry)+Lucas(i,ly)+mod+mod)%mod,ans=(ans+now)%mod;
    return ans;
}
int main()
{
    int xa,ya,xb,yb;
    while(cin>>xa>>ya>>xb>>yb>>mod){
        Prepare(100005);
        cout<<work(xa,ya,xb,yb)<<endl;
    }
    return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值