组合数学
咕咕咕了
容斥原理
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; }
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; }
斯特林反演
【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; }