• [Noip2017]组合数问题


    [Noip2017]组合数问题

    一.前言

    ​ 组合数学没学好阿巴阿巴……题目链接

    二.思路

    ​ 首先给一个结论:杨辉三角和组合数的关系是密不可分的。这两个就是一个东西其实。先来康康一个很显而易见的数学公式。

    [C_m^n=C_{m}^{n-1}+C_{m-1}^{n-1} ]

    大概,还是很容易理解的,吧……稍作解释就是。

    在 n 个数中选 m 个做组合的情况可以从 在前 n-1 个数中就选出 m 个和 在前 n-1个数中选出 m-1 个转移过来(后一种是第 n 个塞进去)

    然后稍加观察,若是把 n 当作行, m 当作列的话,就是杨辉三角。换而言之,(C^i_j) 为杨辉三角的第 i 行,第 j 列的值。

    然后我们再看问题,求的是 k 的倍数的个数,也就是 (\%k=0) 的个数,这里将询问离线,预处理直接(O(1))解决询问。所以预处理一个基于杨辉三角的前缀和数组 (sum[n][m]) 表示答案。转移方程为

    [sum[i][j]=sum[i-1][j]+sum[i][j-1]-sum[i-1][j-1] ]

    然后加一个当前值是不是 (\%k=0) 判断就ok。

    ​ 关于转移方程,可以具体手推,这里给出一个抽象理解。(今天手比较抖)

    框住的表示这一部分的前缀和,现在是在求红框。灰色是重复部分。

    但是这是常规的杨辉三角,计算机里面的杨辉三角,稍微有点点不同,是个方的,所以画图为

    大概是这种感觉,然后可以发现篮框右下角是空点,实际上空点的值应该是空点左边的值,为了方便计算就有一个(sum[i][i+1]=sum[i][i]),后面推就没有问题了。

    三.CODE

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #include<fstream>
    #include<cmath>
    using namespace std;
    int read(){
    	char ch=getchar();
    	int res=0,f=1;
    	for(;ch<'0'||ch>'9';ch=getchar())if(ch=='-')f=-1;
    	for(;ch>='0'&&ch<='9';ch=getchar())res=res*10+(ch-'0');
    	return res*f;
    }
    int t,k,n,m;
    int f[2005][2005],sum[2005][2005];
    int main(){
    	t=read();k=read();
    	for(int i=0;i<=2004;++i)f[i][i]=f[i][0]=1;
    	for(int i=1;i<=2004;++i){
    		for(int j=1;j<=i;++j){
    			f[i][j]=(f[i-1][j]+f[i-1][j-1])%k;//一直膜就对了
    			sum[i][j]=(sum[i-1][j]+sum[i][j-1]-sum[i-1][j-1]);
    			if(!f[i][j])sum[i][j]++;
    		}
    		sum[i][i+1]=sum[i][i];//比较关键的一步
    	}
    	while(t--){
    		n=read();m=read();
    		cout<<sum[n][min(n,m)]<<endl;
    	}
    	return 0;
    } 
    
  • 相关阅读:
    JDBC数据库连接池的实现
    在java项目中引入dll文件
    动态调用WebService(支持SaopHeader)
    获取CPU,硬盘,网卡信息
    js调用本地程序
    ClickOnce发布的一点小经验总结
    为程序设置快捷键
    构建返回友好信息的WebService
    使用DataReader分页的测试
    【bug】internal class GetCountOfEntitiesByCriterionHibernateCallback : IHibernateCallback<int>
  • 原文地址:https://www.cnblogs.com/clockwhite/p/13466384.html
Copyright © 2020-2023  润新知