HDU 3944-DP?(Lucas定理+预处理)

题目地址:HDU 3944

题意:告诉你在一个在杨辉三角中的点(第n行m列),问你从(0,0)点走到该点经过的点最少的权值和(只能向下走或斜着走)。同时对素数p取余

思路:根据已知的那个点(n,m),如果 n/2 >= m ,那么从已知点出发,可以一直往斜的方向走,直到边界,那么 权值和就为 C(n,m)+C(n-1,m-1)……. 然后由组合数的公式两两合并可以得到 C(n+1,m)+(n-m);如果n/2< m,那么就是一直往上走,权值和就为C(n,m)+C(n-1,m)+C(n-2,m)….. 等于C(n+1,m+1)+m。因为同样根据组合数的性质当m<=n/2,m=n-m。所以只剩下第二种情况。

同样用Lucas定理化简,但是样例有1e5组,非常大,开始的时候用传统方法TLE了一发。在这里要预处理一下,将每个数阶乘同10000以内的每个素数的取余都保存下来,到时候直接用。当一个数>=某个素数的时候,对这个素数取余肯定等于0,这相当于一个优化。

#include <stdio.h>
#include <math.h>
#include <string.h>
#include <stdlib.h>
#include <iostream>
#include <sstream>
#include <algorithm>
#include <set>
#include <queue>
#include <stack>
#include <map>
#include <bitset>
#pragma comment(linker, "/STACK:102400000,102400000")
using namespace std;
typedef __int64 LL;
const int inf=0x3f3f3f3f;
const double pi= acos(-1.0);
const double esp=1e-6;
using namespace std;
const int Maxn=1e4+10;
bitset<Maxn>pri;
int prime[Maxn];
int flag[Maxn];
int fac[Maxn][Maxn];
int inv[Maxn][Maxn];
int k=0;
int n,m,p;
void is_prime()
{
    pri.set();
    memset(flag,0,sizeof(flag));
    for(int i=2; i<Maxn; i++) {
        if(pri[i]) {
            prime[++k]=i;
            flag[i]=k;
            for(LL j=i+i; j<Maxn; j+=i)
                pri[j]=0;
        }
    }
}
int modxp(int a,int b,int mod)
{
    int res=1;
    while(b>0) {
        if(b&1)
            res=res*a%mod;
        b=b>>1;
        a=a*a%mod;
    }
    return res;
}
void Init()
{
    for(int i=1; i<=k; i++) {
        fac[i][0]=1;
        inv[i][0]=1;
        for(int j=1; j<prime[i]; j++) {
            fac[i][j]=(fac[i][j-1]*j)%prime[i];
            inv[i][j]=modxp(fac[i][j],prime[i]-2,prime[i]);
        }
    }

}
int C(int n,int m)
{
    if(m>n) return 0;
    if(m==n) return 1;
    int t=flag[p];
    return fac[t][n]*(inv[t][n-m]*inv[t][m]%p)%p;
}

int Lucas(int n,int m)
{
    if(m==0)  return 1;
    return C(n%p,m%p)*Lucas(n/p,m/p)%p;
}

int main()
{
    int icase=1;
    is_prime();
    Init();
    while(~scanf("%d %d %d",&n,&m,&p)) {
        if(m<=n/2) m=n-m;
        printf("Case #%d: %d\n",icase++,((m+Lucas(n+1,m+1)%p)+p)%p);
    }
    return 0;
}

版权声明:本文为博主原创文章,未经博主允许不得转载。

时间: 2024-11-10 00:55:19

HDU 3944-DP?(Lucas定理+预处理)的相关文章

hdu 3944 DP? (Lucas 定理)

仔细观察杨辉三角后可以发现从最高点到第n行第k个数的最短路为c(n+1,k); 根据Lucas定理可以求出,一般来说要求答案模去一个质数p且p的范围不大于10^5则可用Lucas. Lucas(n,m,p)=cm(n%p,m%p)* Lucas(n/p,m/p,p) Lucas(x,0,p)=1; 另外注意当k>n/2时,必须令k=n-k. #include <iostream> #include <cstdio> #include <cstring> #incl

hdu 3944 dp?

DP? Time Limit: 10000/3000 MS (Java/Others)    Memory Limit: 128000/128000 K (Java/Others)Total Submission(s): 1804    Accepted Submission(s): 595 Problem Description Figure 1 shows the Yang Hui Triangle. We number the row from top to bottom 0,1,2,-a

【bzoj2111】[ZJOI2010]Perm 排列计数 dp+Lucas定理

题目描述 称一个1,2,...,N的排列P1,P2...,Pn是Mogic的,当且仅当2<=i<=N时,Pi>Pi/2. 计算1,2,...N的排列中有多少是Mogic的,答案可能很大,只能输出模P以后的值 输入 输入文件的第一行包含两个整数 n和p,含义如上所述. 输出 输出文件中仅包含一个整数,表示计算1,2,?, n的排列中, Mogic排列的个数模 p的值. 样例输入 20 23 样例输出 16 题解 dp+Lucas定理 题目显然小根堆,考虑怎么求以一个节点为根的方案数.根肯定

[CTSC2017][bzoj4903] 吉夫特 [状压dp+Lucas定理]

题面 传送门 思路 一句话题意: 给出一个长度为 n 的序列,求所有长度大于等于2的子序列个数,满足:对于子序列中任意两个相邻的数 a和 b (b 在 a 前面),\(C_a^b mod 2=1\),答案对1e9+7取模 显然膜2余1是个非常特殊的性质,应当好好加以利用 和组合数取模有关的东西,有Lucas定理,因此我们来试着推一推 \(C_n^m%2=C_{n%2}^{m%2}\ast C_{n/2}^{m/2}\) 这个玩意的意义,显然就是把n和m转成二进制,那么只要没有某一位上n是0m是1

bzoj 2111 [ZJOI2010]Perm 排列计数(DP+lucas定理)

[题目链接] http://www.lydsy.com/JudgeOnline/problem.php?id=2111 [题意] 给定n,问1..n的排列中有多少个可以构成小根堆. [思路] 设f[i]为以i为根的方案数,设l为左子树大小r为右子树大小,则有: f[i]=C(i-1,l)*f[l]*f[r] 因为是个堆,所以子树大小都是确定的,可以直接递推得到. 其中C(n,m) nm比较大,可以用lucas定理求. 模型建立的重要性可知一二... [代码] 1 #include<cstdio>

【bzoj3782】上学路线 dp+容斥原理+Lucas定理+中国剩余定理

题目描述 小C所在的城市的道路构成了一个方形网格,它的西南角为(0,0),东北角为(N,M).小C家住在西南角,学校在东北角.现在有T个路口进行施工,小C不能通过这些路口.小C喜欢走最短的路径到达目的地,因此他每天上学时都只会向东或北行走:而小C又喜欢走不同的路径,因此他问你按照他走最短路径的规则,他可以选择的不同的上学路线有多少条.由于答案可能很大,所以小C只需要让你求出路径数mod P的值. 输入 第一行,四个整数N.M.T.P. 接下来的T行,每行两个整数,表示施工的路口的坐标. 输出 一

组合数(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

HDU 3037 Saving Beans 多重集合的结合 lucas定理

题目链接: http://acm.hdu.edu.cn/showproblem.php?pid=3037 题目描述: 要求求x1 + x2 + x3 + ...... + xn <= m 非负整数解的个数, 结果对P取模, 输入的变量是n, m, p, P一定是素数 解题思路: x1 + ... + xn = m 非负整数解的个数是C(n+m-1, n) , 所以答案就是 C(n+0-1, 0) + C(n+1-1, 1) + ...... C(n+m-1, n) 对P取模, 由于组合数公式C(

HDU 3037 Saving Beans (Lucas定理)

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=3037 推出公式为C(n + m, m) % p, 用Lucas定理求大组合数取模的值 代码: #include <stdio.h> #include <string.h> #include <algorithm> using namespace std; int t; long long n, m, p; long long pow(long long n, long lo