组合数(Lucas定理) + 快速幂 --- HDU 5226 Tom and matrix

Tom and matrix

Problem‘s Link:   http://acm.hdu.edu.cn/showproblem.php?pid=5226



Mean:

题意很简单,略。

analyse:

直接可以用Lucas定理+快速幂水过的,但是我却作死的用了另一种方法。

方法一:Lucas定理+快速幂水过

方法二:首先问题可以转化为求(0,0),(n,m)这个子矩阵的所有数之和。画个图容易得到一个做法,对于n<=m,答案就是2^0+2^1+...+2^m=2^(m+1)-1,对于n>m,答案由两部分构成,一部分是2^(m+1)-1,另一部分是sigma i:m+1->n f[i][m],f[i][m]表示第i行前m列的数之和,f数组存在如下关系,f[i][m]=f[i-1][m]*2-C[i-1][m],f[m][m]=2^m。还有另一种思路:第i列的所有数之和为C(i,i)+C(i+1,i)+...+C(n,i)=C(n+1,i+1),于是答案就是sigma i:0->min(n,m) C(n+1,i+1)。

Lucas定理:由于题目给定的模是可变的质数,且质数可能很小,那么就不能直接用阶乘和阶乘的逆相乘了,需要用到Lucas定理,公式:C(n,m)%P=C(n/P,m/P)*C(n%P,m%P),c(n,m)=0(n<m)。当然最终还是要预处理阶乘和阶乘的逆来得到答案。复杂度O(nlogP+nlogn)

Time complexity: O(n)

Source code: 

Lucas定理+快速幂

/*
* this code is made by crazyacking
* Verdict: Accepted
* Submission Date: 2015-05-21-23.28
* Time: 0MS
* Memory: 137KB
*/
#include <queue>
#include <cstdio>
#include <set>
#include <string>
#include <stack>
#include <cmath>
#include <climits>
#include <map>
#include <cstdlib>
#include <iostream>
#include <vector>
#include <algorithm>
#include <cstring>
#define  LL long long
#define  ULL unsigned long long
using namespace std;

const int maxn=100010;
struct cell
{
        int x,y;
        bool operator<(cell c) const
        {
                return x==c.x?(y<c.y):(x<c.x);
        }
}p[2];
LL mod;
LL Pow(LL a,LL b)
{
    LL ret=1;
    a%=mod;
    while(b)
    {
        if(b&1) ret=ret*a%mod;
        a=a*a%mod;
        b>>=1;
    }
    return ret%mod;
}
namespace lucas
{
        LL A[maxn],inv[maxn];
        void init()
        {
            A[0]=1,A[1]=1;
            inv[1]=1;inv[0]=1;
            for(int i=2;i<maxn;i++)
            {A[i]=A[i-1]*(LL)i%mod;inv[i]=Pow(A[i],mod-2);}
        }
        LL Lucas(LL a,LL b)
        {

            if(a<b) return 0;
            if(a<mod&&b<mod) return (A[a]*inv[b]%mod)*inv[a-b]%mod;
            return Lucas(a/mod,b/mod)*Lucas(a%mod,b%mod)%mod;
        }
}
using namespace lucas;

int main()
{
        ios_base::sync_with_stdio(false);
        cin.tie(0);
        while(cin>>p[0].x>>p[0].y>>p[1].x>>p[1].y>>mod)
        {
                if(p[0].y>p[0].x&&p[1].y>p[1].x&&p[0].y>p[1].x) {printf("0\n");continue;}
                init();
                sort(p,p+2);
                if(!(p[0].x<=p[1].x && p[0].y<=p[1].y))
                {
                        int x1=p[0].x,y1=p[0].y,x2=p[1].x,y2=p[1].y;
                        p[0].x=x1,p[0].y=y2,p[1].x=x2,p[1].y=y1;
                }
                LL sta=p[0].x,en=p[1].x,h=p[0].y,ans=0;
                while(h<=p[1].y && sta<=en )
                {
                        if(sta<h) sta=h;
                        ans=(ans+Lucas(en+1,h+1)-Lucas(sta,h+1)+mod)%mod;
                        h++;
                }
                printf("%lld\n",ans);

        }
        return 0;
}
/*

*/

方法二:

/*
* this code is made by crazyacking
* Verdict: Accepted
* Submission Date: 2015-05-21-02.58
* Time: 0MS
* Memory: 137KB
*/
#include <queue>
#include <cstdio>
#include <set>
#include <string>
#include <stack>
#include <cmath>
#include <climits>
#include <map>
#include <cstdlib>
#include <iostream>
#include <vector>
#include <algorithm>
#include <cstring>
#define  LL long long
#define  ULL unsigned long long
using namespace std;
struct cell
{
        int x,y;
        bool operator<(cell c) const
        {
                return x==c.x?(y<c.y):(x<c.x);
        }
}p[2];
LL mod;
LL inv[101000],A[101000];
inline LL Pow(LL a,LL b)
{
    LL ret=1;
    a%=mod;
    while(b)
    {
        if(b&1) ret=ret*a%mod;
        a=a*a%mod;
        b>>=1;
    }
    return (ret-1)%mod;
}

void init()
{
    A[0]=1,A[1]=1;
    inv[1]=1;inv[0]=1;
    for(int i=2;i<101000;i++)
    {A[i]=A[i-1]*(LL)i%mod;inv[i]=Pow(A[i],mod-2);}
}
LL Lucas(LL a,LL b)
{
    if(a<b) return 0;
    if(a<mod&&b<mod) return (A[a]*inv[b]%mod)*inv[a-b]%mod;
    return Lucas(a/mod,b/mod)*Lucas(a%mod,b%mod)%mod;
}

inline LL Pow(LL b)
{
        b=b+1;
        if(b<0) return 0;
    LL a=2;
    LL ret=1;
    a%=mod;
    while(b)
    {
        if(b&1) ret=ret*a%mod;
        a=a*a%mod;
        b>>=1;
    }
    return (ret-1)%mod;
}

inline int calc_Matrix(int x,int y)
{
        if(x<0||y<0) return 0;
        if(x<=y)
                return Pow(x);
        else
        {
                LL sum1=Pow(y);
                LL tmp=Pow(y)-Pow(y-1);
                LL sum2=0;
                for(int i=y+1;i<=x;++i)
                {
                      tmp=tmp*2-(int)Lucas((LL)i-1,(LL)y);
                      tmp%=mod;
                      sum2+=tmp;
                      sum2%=mod;
                }
                return (sum1+sum2)%mod;
        }
}
int main()
{
        ios_base::sync_with_stdio(false);
        cin.tie(0);
        while(cin>>p[0].x>>p[0].y>>p[1].x>>p[1].y>>mod)
        {
                if(p[0].y>p[0].x&&p[1].y>p[1].x&&p[0].y>p[1].x) {printf("0\n");continue;}
                init();
                sort(p,p+2);
                if(!(p[0].x<=p[1].x && p[0].y<=p[1].y))
                {
                        int x1=p[0].x,y1=p[0].y,x2=p[1].x,y2=p[1].y;
                        p[0].x=x1,p[0].y=y2,p[1].x=x2,p[1].y=y1;
                }
                cout<<(calc_Matrix(p[1].x,p[1].y)-calc_Matrix(p[0].x-1,p[1].y)-calc_Matrix(p[1].x,p[0].y-1)+calc_Matrix(p[0].x-1,p[0].y-1))%mod<<endl;
        }
        return 0;
}
/*

*/

时间: 2024-10-10 13:38:10

组合数(Lucas定理) + 快速幂 --- HDU 5226 Tom and matrix的相关文章

hdu 5226 Tom and matrix

题意: Tom放学回家的路上,看到天空中出现一个矩阵.Tom发现,如果矩阵的行.列从0开始标号,第i行第j列的数记为a[i][j],那么a[i][j]=C(i,j) 如果i < j,那么a[i][j]=0 Tom突发奇想,想求一个矩形范围((x1,y1),(x2,y2))内所有数的和.Tom急着回家,当然不会自己算,所以就把任务交给你了. 因为数可能很大,答案对一个质数p取模. 限制: 0 <= x1 <= x2 <=1e5 0 <= y1 <= y2 <=1e5

HDU 5226 Tom and matrix(组合数学+Lucas定理)

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=5226 题意:给一个矩阵a,a[i][j] = C(i,j)(i>=j) or 0(i < j),求(x1,y1),(x2,y2)这个子矩阵里面的所有数的和. 思路:首先可以推导出一个公式C(n,i)+C(n + 1,i)+...+C(m,i) = C(m + 1,i + 1) 知道了这个公式,就可以将子矩阵里每行(或每列)的和值表示成组合数的差值,现在的关键是求出C(n,m)(mod p). 由于

2014多校第一场 I 题 || HDU 4869 Turn the pokers(费马小定理+快速幂模)

题目链接 题意 : m张牌,可以翻n次,每次翻xi张牌,问最后能得到多少种形态. 思路 :0定义为反面,1定义为正面,(一开始都是反), 对于每次翻牌操作,我们定义两个边界lb,rb,代表每次中1最少时最少的个数,rb代表1最多时的个数.一张牌翻两次和两张牌翻一次 得到的奇偶性相同,所以结果中lb和最多的rb的奇偶性相同.如果找到了lb和rb,那么,介于这两个数之间且与这两个数奇偶性相同的数均可取到,然后在这个区间内求组合数相加(若lb=3,rb=7,则3,5,7这些情况都能取到,也就是说最后的

hdu 4704 Sum (费马小定理+快速幂)

//(2^n-1)%mod //费马小定理:a^n ≡ a^(n%(m-1)) * a^(m-1)≡ a^(n%(m-1)) (mod m) # include <stdio.h> # include <algorithm> # include <string.h> # define mod 1000000007 using namespace std; __int64 pow(__int64 n) { __int64 p=1,q=2; while(n) { if(n%

hdu 4704 费马小定理+快速幂

题意就是:做整数拆分,答案是2^(n-1) 由费马小定理可得:2^n % p = 2^[ n % (p-1) ]  % p 当n为超大数时,对其每个数位的数分开来加权计算 当n为整型类型时,用快速幂的方法求解 #include<iostream> #include<cstdio> #include<cstring> #include<string> #include<algorithm> using namespace std; const in

HDU 4704 Sum(隔板原理+组合数求和公式+费马小定理+快速幂)

题目传送:http://acm.hdu.edu.cn/showproblem.php?pid=4704 Problem Description Sample Input 2 Sample Output 2 Hint 1. For N = 2, S(1) = S(2) = 1. 2. The input file consists of multiple test cases. 题意是输入一个N,求N被分成1个数的结果+被分成2个数的结果+...+被分成N个数的结果,N很大 1.隔板原理 1~N有

【组合数+Lucas定理】2017多校训练七 HDU 6129 Just do it

http://acm.hdu.edu.cn/showproblem.php?pid=6129 [题意] 对于一个长度为n的序列a,我们可以计算b[i]=a1^a2^......^ai,这样得到序列b 重复这样的操作m次,每次都是从上次求出的序列a得到一个新序列b 给定初始的序列,求重复m次操作后得到的序列 [思路] 假定n=5,我们模拟一次可以发现,经过m次操作后a1在b1......bn中出现的次数为: m=0: 1 0 0 0 0 m=2: 1 2 3 4 5 m=3: 1 3 6 10 1

UVALive 7040 Color (容斥原理+逆元+组合数+费马小定理+快速幂)

题目:传送门. 题意:t组数据,每组给定n,m,k.有n个格子,m种颜色,要求把每个格子涂上颜色且正好适用k种颜色且相邻的格子颜色不同,求一共有多少种方案,结果对1e9+7取余. 题解: 首先可以将m 与后面的讨论分离.从m 种颜色中取出k 种颜色涂色,取色部分有C(m, k) 种情况: 然后通过尝试可以发现,第一个有k种选择,第二个因不能与第一个相同,只有(k-1) 种选择,第三个也只需与第二个不同,也有(k-1) 种选择.总的情况数为k ×(k-1)^(n-1).但这仅保证了相邻颜色不同,总

HDU4869:Turn the pokers(费马小定理+快速幂)

Problem Description During summer vacation,Alice stay at home for a long time, with nothing to do. She went out and bought m pokers, tending to play poker. But she hated the traditional gameplay. She wants to change. She puts these pokers face down,