组合数学

 咕咕咕了

容斥原理 

ARC096E

钦定有 $i$ 个数出现次数小于 $1$ 的答案为 $F(i)$ ,则 $Ans=\sum_{i=0}^n (-1)^i\cdot F(i)$ 。

而对于 $F(i)$ 可以枚举用了几个集合,计数即可。时间复杂度 $O(n^2)$ 。

#include<iostream>
#include<cstring>
#include<cstdio>
#include<cstring>
#include<vector>
#include<queue>
#include<algorithm>
#include<climits>
#include<bitset>
#define pii pair<int,int>
#define pb push_back
#define mp make_pair
#define fi first
#define se second
#define int long long
using namespace std;
inline int read(){
    int f=1,ans=0;char c=getchar();
    while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
    while(c>='0'&&c<='9'){ans=ans*10+c-'0';c=getchar();}
    return f*ans;
}
const int MAXN=3011;
int f[MAXN][MAXN],fac[MAXN],pw2[MAXN],pw22[MAXN],ifac[MAXN],inv[MAXN],N,mod,F[MAXN],Ans,pw=-1;
int ksm(int a,int b){int ans=1;while(b){if(b&1) ans*=a,ans%=mod;a*=a,a%=mod;b>>=1;}return ans;}
int C(int a,int b){return fac[a]*ifac[b]%mod*ifac[a-b]%mod;}
signed main(){
    N=read(),mod=read();fac[0]=fac[1]=ifac[0]=ifac[1]=inv[1]=1;pw2[0]=pw22[0]=1;for(int i=1;i<MAXN;i++) pw2[i]=pw2[i-1]*2%mod,pw22[i]=pw22[i-1]*2%(mod-1);
    for(int i=2;i<MAXN;i++) fac[i]=fac[i-1]*i%mod,inv[i]=(mod-mod/i)*inv[mod%i]%mod,ifac[i]=ifac[i-1]*inv[i]%mod;
    f[0][0]=1;for(int i=1;i<MAXN;i++){for(int j=1;j<=i;j++) f[i][j]=f[i-1][j-1]+j*f[i-1][j],f[i][j]%=mod;}
    for(int i=0;i<=N;i++) for(int j=0;j<=i;j++) {F[i]+=C(N,i)*f[i+1][j+1]%mod*ksm(2,pw22[N-i])%mod*ksm(pw2[N-i],j)%mod,F[i]%=mod;}
    for(int i=0;i<=N;i++) pw*=-1,Ans=(Ans+pw*F[i]+mod)%mod;
    printf("%lld\n",Ans);return 0;
}
View Code

 

ARC093F

可以发现 $1$ 放在序列何处均不影响答案,计算将 $1$ 放在第一个的答案。

则 $1$ 要挑战的选手为 ${p_2},{p_3,p_4},{p_5,p_6,p_7,p_8},...,{p_{2^{n-1}+1},p_{2^n}}$ 。集合表示为里面 $p$ 的最小值。

则现在的问题变为了每个集合的最小值都不为 $A$ 中的数,由于 $n,m\leq 16$,考虑容斥。

设 $F(S)$ 表示钦定 $n$ 个集合中最小值是否为 $A$ 的情况。从大到小 $dp$ 即可。 

 

#include<iostream>
#include<cstring>
#include<cstdio>
#include<cstring>
#include<vector>
#include<queue>
#include<algorithm>
#include<climits>
#include<bitset>
#define pii pair<int,int>
#define pb push_back
#define mp make_pair
#define fi first
#define se second
#define int long long
#define mod 1000000007
using namespace std;
inline int read(){
    int f=1,ans=0;char c=getchar();
    while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
    while(c>='0'&&c<='9'){ans=ans*10+c-'0';c=getchar();}
    return f*ans;
}
const int MAXN=17;
int f[MAXN][1<<MAXN],fac[1<<MAXN],N,M,A[MAXN],Lim,ifac[1<<MAXN],inv[1<<MAXN],Ans;
bool cmp(int a,int b){return a>b;}
int C(int a,int b){return a<b?0:fac[a]*ifac[a-b]%mod*ifac[b]%mod;}
int lowbit(int x){return x&-x;}
int cont(int x){int c=0;while(x){x-=lowbit(x);c++;}return c;}
signed main(){
    //freopen("1.in","r",stdin);
    N=read(),M=read();for(int i=1;i<=M;i++) A[i]=read();sort(A+1,A+M+1,cmp);Lim=(1<<N);
    fac[0]=fac[1]=inv[1]=ifac[0]=ifac[1]=1;for(int i=2;i<=Lim;i++) fac[i]=fac[i-1]*i%mod,inv[i]=(mod-mod/i)*inv[mod%i]%mod,ifac[i]=ifac[i-1]*inv[i]%mod;f[0][0]=1;
    for(int i=1;i<=M;i++){
        for(int j=0;j<Lim;j++){
            f[i][j]+=f[i-1][j];
            for(int k=0;k<N;k++){
                if((j&(1<<k))) continue;
                f[i][j|(1<<k)]+=f[i-1][j]*C((1<<N)-A[i]-j,(1<<k)-1)%mod*fac[(1<<k)]%mod,f[i][j]%=mod;
            }
        }
    }
    for(int i=0;i<Lim;i++){
        int pw=(cont(i)&1)?-1:1;
        Ans=(Ans+pw*f[M][i]*fac[Lim-1-i]+mod)%mod;
    }printf("%lld\n",Ans*(1<<N)%mod);return 0;
}
View Code

 

斯特林反演

【2018 雅礼集训】方阵

设 $f(m)$ 表示 $n$ 行 $m$ 列,且每行均不同的方案数,则 $f(m)=(c^m)^{\underline n}$ 。

$g(m)$ 表示 $n$ 行 $m$ 列且每行每列均不同的方案数,则 $f(m)=\sum_{i=0}^m S(m,i)\cdot g_i$,其中 $S$ 为第二类斯特林数。

直接斯特林反演。时间复杂度 $O(nm)$ 。

#include<iostream>
#include<cstring>
#include<cstdio>
#include<cstring>
#include<vector>
#include<queue>
#include<algorithm>
#include<climits>
#include<bitset>
#define pii pair<int,int>
#define pb push_back
#define mp make_pair
#define fi first
#define se second
#define mod 1000000007
#define int long long
using namespace std;
inline int read(){
    int f=1,ans=0;char c=getchar();
    while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
    while(c>='0'&&c<='9'){ans=ans*10+c-'0';c=getchar();}
    return f*ans;
}
const int MAXN=4e3+11;
int N,M,C,Ans,F[MAXN],f[MAXN][MAXN];
int ksm(int a,int b){int ans=1;while(b){if(b&1) ans*=a,ans%=mod;a*=a,a%=mod;b>>=1;}return ans;}
signed main(){
    N=read(),M=read(),C=read();
    f[0][0]=1;for(int i=1;i<MAXN;i++) for(int j=1;j<=i;j++) f[i][j]=(i-1)*f[i-1][j]+f[i-1][j-1],f[i][j]%=mod;
    for(int i=0;i<=M;i++){
        int FF=ksm(C,i),res=1;
        for(int j=1;j<=N;j++) res*=(FF-j+1),res%=mod;
        F[i]=res;
    }
    for(int i=0;i<=M;i++){
        int opt=(((M-i)&1)?-1:1);
        Ans=(Ans+opt*f[M][i]*F[i]%mod+mod)%mod;
    }printf("%lld\n",Ans);return 0;
}
View Code

 

posted @ 2020-09-27 18:35  siruiyang_sry  阅读(66)  评论(0编辑  收藏  举报