• 2021.05.10讲题


    (2021.05.10) 基础数学和组合数学2 (B G H by DReamLion)
    upd:排队化简过程

    B P2822 组合数问题

    给出组合数公式,求满足0<=i<=n,0<=j<=min(i,m)的条件下,i选j的组合数是k的倍数有多少种可能
    一看数据范围,n<=2000,m<=2000,t<=1e4,直接套公式是必然不可能过的
    因为是多组数据,所以先看能不能预处理一下,然后每组只查询就可以了
    怎么预处理?我们可以用杨辉三角来做,因为由杨辉三角的性质可知,第n行m个数可表示为C(n-1,m-1),正好可以用来求组合数,算杨辉三角的时候顺便把k模掉就行了
    还需要用到二维前缀和,用f[][]数组来存前缀和:

    f[i][j]=(f[i-1][j]+f[i][j-1]-f[i-1][j-1]);
    

    注意输出的一个小细节:

    if(n<m) printf("%lld
    ",f[n][n]);//n选m,但是m比n还大,所以当成n选n就行 
    

    因为题目没说m一定大于n,所以如果不加这句会被卡掉40pts

    #include<iostream>
    #include<cstdio>
    #include<cstdlib>
    #define maxn 2010
    #define ll long long
    using namespace std;
    template<typename T>
    inline void read(T &x){
    	x=0; bool flag=0; char c=getchar();
    	for(;!isdigit(c);c=getchar()) if(c=='-') flag=1;
    	for(;isdigit(c);c=getchar()) x=x*10+(c^48);
    	if(flag) x=-x;
    }
    
    ll t,k,n,m;
    ll s[maxn][maxn],f[maxn][maxn];
    
    void yanghui(){
    	s[0][0]=s[1][0]=s[1][1]=1;//杨辉三角的前两行 
    	for(int i=2;i<=2000;i++){
    		s[i][0]=1;
    		for(int j=1;j<=i;j++){
    			s[i][j]=(s[i-1][j-1]%k+s[i-1][j]%k)%k;
    			f[i][j]=(f[i-1][j]+f[i][j-1]-f[i-1][j-1]);//前缀和,上加左减左上加自己 
    			if(s[i][j]==0) f[i][j]++;//满足结论,前缀和计数+1 
    		}
    		f[i][i+1]=f[i][i];//继承 ,便于调用 
    	} 
    }
    
    int main(){
    	read(t),read(k);
    	yanghui();
    	for(int i=1;i<=t;i++){
    		read(n),read(m);
    		if(n==0){printf("1
    "); continue;}
    		if(n<m) printf("%lld
    ",f[n][n]);//n选m,但是m比n还大,所以当成n选n就行 
    		else printf("%lld
    ",f[n][m]);
    	}
    	return 0;
    }
    

    G P3223 排队

    插空法
    直接算不好算,我们可以考虑老师不相邻=不考虑老师相邻-老师相邻
    因为男生能相邻,所以把男生当背景板,把女生插空
    不考虑老师相邻->把老师也扔进背景板里(大雾:

    [A_{n+2}^{n+2}*A_{m}^{m}*C_{n+3}^{m} ]

    考虑老师相邻->俩老师捆一起,相当于一个男生:

    [A_{2}^{2}*A_{n+1}^{n+1}*A_{m}^{m}*C_{n+2}^{m} ]

    化简:

    [frac{(n+2)!m!(n+3)!}{(n+3-m)!m!}- frac{2(n+1)!m!(n+2)!}{(n+2-m)!m!} ]

    [frac{(n+2)(n+1)!(n+3)(n+2)!}{(n+3-m)(n+2-m)!}- frac{2(n+1)!(n+2)!}{(n+2-m)!} ]

    [frac{(n+1)!(n+2)!}{(n+2-m)!}(frac{(n+2)(n+3)}{(n+3-m)}-2) ]

    [frac{(n+1)!(n+2)!}{(n+2-m)!}frac{n^2+3n+2m}{n+3-m} ]

    得:

    [prod_{i=n+4-m}^{n+2} i*(n+1)!*(n^2+3*n+2*m) ]

    注意到答案非常大会爆long long(long long只有10pts),我们必须写高精

    #include<iostream>
    #include<cstdio>
    #include<cstdlib>
    #include<vector>
    #define ll long long
    using namespace std;
    template<typename T>
    inline void read(T &x){
        x=0;bool flag=0;char c=getchar();
        for(;!isdigit(c);c=getchar()) if(c=='-') flag=1;
        for(;isdigit(c);c=getchar()) x=x*10+(c^48);
        if(flag) x=-x;
    } 
    
    ll n,m,tmp;
    vector<int> ans;
    
    vector<int> mul(vector<int> &A,int b){
    	vector<int> C;
    	int t=0;
    	for(int i=0;i<A.size()||t;i++){
    		if(i<A.size())t+=A[i]*b;
    		
    		C.push_back(t%10);
    		t/=10;
    	}
    	while(C.size()>1&&C.back()==0) C.pop_back();
    	return C;
    }
    
    int main(){
    	read(n),read(m);
    	tmp=n*n+2*m+3*n;
    	ans.push_back(1);
    	for(int i=1;i<=n+1;i++) ans=mul(ans,i);
    	for(int i=n-m+4;i<=n+2;i++) ans=mul(ans,i);
    	ans=mul(ans,tmp);
    	int len=ans.size()-1;
    	for(;len>=0;len--)printf("%d",ans[len]);
    	printf("
    ");
    	return 0;
    }
    

    H P3166 数三角形

    和上一题类似,可以考虑总数-三点共线的情况
    三点共线一共三种可能:在同一行,在同一列,在同一斜线上
    注意给的是网格的n,m,格点数要++(下面就默认已经n++,m++了;代码里没在开始加,写的麻烦了)
    因为要选三个格点,所以n选m就相当于n*m选3,这是总数
    先减在同一行和在同一列的情况,因为这两种好算:

    if(m>=2) ans-=(n+1)*mul(m+1);
    if(n>=2) ans-=(m+1)*mul(n+1);
    

    至于斜线上的情况,有个结论:
    对于两点(a,b)(c,d),其中a>c,b>d,
    在这两点的线段上有gcd(a-c,b-d)-1个整点
    所以我们枚举线段两头的点,让第三个点在这条线段上,就能求了
    为了优化复杂度,我们利用矩形的对称性,只枚举一个端点和第三个点,然后*2就得到了所有方案

    #include<iostream>
    #include<cstdio>
    #include<cstdlib>
    #define maxn 2010
    #define ll long long
    using namespace std;
    template<typename T>
    inline void read(T &x){
    	x=0; bool flag=0; char c=getchar();
    	for(;!isdigit(c);c=getchar()) if(c=='-') flag=1;
    	for(;isdigit(c);c=getchar()) x=x*10+(c^48);
    	if(flag) x=-x;
    }
    
    ll m,n,ans;
    
    ll mul(ll a){
    	ll res;
    	res=(a)*(a-1)*(a-2)/(1*2*3);
    	return res;
    }
    
    int gcd(int a,int b){
    	return (b==0)?a:gcd(b,a%b);
    }
    
    int main(){
    	read(m),read(n);
    //	m++;n++;
    	ans=mul((m+1)*(n+1));
    //	cout<<"*"<<ans<<endl;
    	if(m>=2) ans-=(n+1)*mul(m+1);
    	if(n>=2) ans-=(m+1)*mul(n+1);
    //	cout<<"**"<<ans<<endl;
    	for(int i=1;i<=m;i++)
    		for(int j=1;j<=n;j++)
    			ans-=2*(m-i+1)*(n-j+1)*(gcd(i,j)-1);
    	printf("%lld
    ",ans);
    	return 0;
    }
    
  • 相关阅读:
    项目架构工具选择
    idea 引入本地jar包
    java 二维/三维/多维数组
    Windows 远程连接
    SQL SERVER 本地同步数据到远程数据服务器
    利用sp_addlinkedserver实现远程数据库链接
    ORACLE 手动添加时间分区
    ORACLE 时间段
    shiro异常简述
    kvm虚拟机克隆
  • 原文地址:https://www.cnblogs.com/DReamLion/p/14752972.html
Copyright © 2020-2023  润新知