1. 程式人生 > >bzoj 2339: [HNOI2011]卡農

bzoj 2339: [HNOI2011]卡農

class names noi 集合相同 down 相同 直接 cpp 位置

Description

技術分享圖片

Solution

比較難想....
我們先考慮去掉無序的這個條件,改為有序,最後除 \(m!\) 即可
\(f[i]\) 表示前\(i\)個合法集合的方案數
明確一點:
如果前\(i-1\)個集合已經確定,並且前\(i\)個是合法的,那麽第\(i\)就是確定的,所以是一一對應的關系,如果不考慮重復和空集的情況,那麽總方案數就是 \(A_{2^{n}-1}^{i-1}\)
考慮去掉不合法的:
1.當前集合為空集,方案數為 \(f[i-1]\)
2.有兩個集合相同,那麽去掉這兩個集合的方案數是 \(f[i-2]\),由於重復的那個位置可以取 \(i-1\) 個位置,且只與這個集合重復,而不與其他集合重復,所以兩個集合可以取 \(2^{n}-1-(i-2)\)


然後直接遞推即可,\(log\)求逆的話,復雜度就是 \(O(n*logn)\)

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1e6+5,mod=1e8+7;
int qm(int x,int k){
  int sum=1;
  while(k){
    if(k&1)sum=1ll*sum*x%mod;
    x=1ll*x*x%mod;k>>=1;
  }
  return sum;
}
int f[N],Fac[N];
int main(){
  freopen("pp.in"
,"r",stdin); freopen("pp.out","w",stdout); int n,m,x,t,jc=1; cin>>n>>m; t=qm(2,n);Fac[1]=x=(t-1+mod)%mod; Fac[0]=1; for(int i=1;i<=m;i++) jc=1ll*i*jc%mod,Fac[i]=1ll*Fac[i-1]*x%mod,x=(x-1+mod)%mod; f[0]=1;f[1]=0; for(int i=2;i<=m;i++){ f[i]=Fac[i-1
]; f[i]=(f[i]-f[i-1]-1ll*f[i-2]*(i-1)%mod*(t-1-(i-2)))%mod; } if(f[m]<0)f[m]+=mod; f[m]=1ll*f[m]*qm(jc,mod-2)%mod; cout<<f[m]<<endl; return 0; }

bzoj 2339: [HNOI2011]卡農