zoukankan      html  css  js  c++  java
  • BZOJ 2339 【HNOI2011】 卡农

    题目链接:卡农

      听说这道题是经典题?

      首先明确一下题意(我在这里纠结了好久):有(n)个数,要求你选出(m)个不同的子集,使得每个数都出现了偶数次。无先后顺序。

      这道题就是一道数学题。显然我们可以强制有先后顺序,只需要在最后除掉一个(m!)即可。令(f_i)表示选出(i)个子集的方案数,我们来考虑一下怎么算。

      由于总的方案数很好计算,选出(i)个子集的方案就是(A^{i-1}_{2^n-1}),因为一旦选出了前(i-1)个,第(i)个就已经确定了。

      我们这样选,可以保证每个数出现了偶数次,但是并不能够保证选出的不是空集以及集合不重复。所以我们来看看不合法的情况有多少。

      第一种情况,如果前(i-1)个就是一个合法方案,那么第(i)个只能是空集。这种情况显然不合法,方案数是(f_{i-1})。

      第二种情况,第(i)个集合和之前任意一个冲突了就不行。由于另外还剩(i-2)个集合未确定,所以这部分方案数为((i-1)f_{i-2}(2^n-1-(i-2)))。第(i)个集合可选的方案数为(2^n-1-(i-2)),然后和另外(i-2)个一起排列一下还要乘上(i-1)。

      所以这道题就做完了。

      下面贴代码:

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<cmath>
    #define File(s) freopen(s".in","r",stdin),freopen(s".out","w",stdout)
    #define maxn 1000010
    #define mod 100000007
    
    using namespace std;
    typedef long long llg;
    
    int n,m,nci;
    llg f[maxn],g[maxn],jie;
    
    llg mi(llg a,int b){
    	llg s=1;
    	while(b){
    		if(b&1) s=s*a%mod;
    		a=a*a%mod; b>>=1;
    	}
    	return s;
    }
    
    int main(){
    	File("a");
    	scanf("%d %d",&n,&m);
    	nci=(mi(2,n)-1+mod)%mod; g[0]=jie=1;
    	for(int i=1;i<=m;i++) g[i]=g[i-1]*(nci-i+1)%mod;
    	for(int i=1;i<=m;i++) jie*=i,jie%=mod;
    	for(int i=3;i<=m;i++){
    		f[i]=g[i-1]-f[i-1];
    		f[i]-=(i-1)*f[i-2]%mod*(nci-i+2)%mod;
    		f[i]%=mod; if(f[i]<0) f[i]+=mod;
    	}
    	printf("%lld",f[m]*mi(jie,mod-2)%mod);
    	return 0;
    }
    
  • 相关阅读:
    Kth Ancestor 第k个祖先问题
    centOS 6.4挂载centOS分区
    上阶段学习总结
    code testing
    Helo~
    leetcode--Maximum Subarray
    leetcode--Climbing Stairs
    leetcode--Search Insert Position
    leetcode--Best Time to Buy and Sell Stock III
    leetcode--Best Time to Buy and Sell Stock II
  • 原文地址:https://www.cnblogs.com/lcf-2000/p/6618151.html
Copyright © 2011-2022 走看看