題目描述:
題目分析:
將拆分爲質數的冪次後可以完全揹包求出總方案數,但是這樣可能相等。
考慮容斥算出互不相同的方案數,最後除以
這個容斥並不是一般的“總方案 - 滿足一個性質 + 滿足兩個性質”那種容斥,這樣算會發現有很多大小相同的相等集合時根本沒法算。
需要將個數劃分成個集合,強制每個集合中的數相同計算方案。但是容斥係數並不好找。
有經驗的人說我們可以給每個集合一個與大小相關的容斥係數,並設一個劃分的容斥係數爲各個集合的容斥係數之積。
對於一個劃分,如果它包含大小大於1的集合,我們希望最後相等集合恰好爲這個劃分的方案最後被計算的次數爲0;否則我們希望它被計算的次數爲1。
考慮對於一個恰好有個集合相等的方案,記爲,在我們枚舉集合劃分的時候會枚舉到它的子劃分(子劃分包含自身),那麼最後相等集合恰好爲這個劃分的方案在枚舉子劃分時被計算的次數是
我們希望對於的集合,它的子劃分的容斥係數之和爲0,這樣只要個集合中有一個大小大於1,那麼它被計算的次數就是0.
據此我們可以得出的計算式:
相當於是在枚舉的子劃分中1號點所在的集合大小。
然後就可以算了,如果暴力枚舉集合劃分然後揹包,複雜度是的,但是我們實際上只需要知道一個集合劃分中每個集合的和集合大小,就可以計算揹包的方案數和容斥係數。而且注意到意味着本質不同的拆分方案遠達不到,當時的整數拆分方案是。
所以我們用來表示一個集合劃分,在外層套上記錄該劃分的方案數。
但是這樣的比較函數會很慢,所以我們需要求出集合劃分的值來比較,於是可以自定義一個,將數組之後求出值即可。
Code(令人驚奇的是代碼中的(取模)函數如果改爲add(int &a,int b){(a+=b)>=mod&&(a-=mod);}會慢上600ms,數組預先sort會快上100ms):
#include<bits/stdc++.h>
#define maxn 31
#define fi first
#define se second
using namespace std;
const int mod = 1e9+7;
int n,m,fac[maxn],inv[maxn],a[maxn],b[maxn],e[10005],cnt,A[maxn],num,f[10005];
bool vis[10005];
typedef pair<int,int> pii;
typedef unsigned long long ULL;
ULL pw[2*maxn];
struct Set{
pii a[maxn]; ULL s;
Set(){memset(a,0,sizeof a),s=0;}
void gethash(){
sort(a,a+31,greater<pii>()),s=0;
for(int i=0;i<30&&a[i].fi;i++) s+=a[i].fi*pw[i]+a[i].se*pw[i+30];
}
bool operator < (const Set &p)const{return s<p.s;}
};
map<Set,int>dp[2];//divide of set and corresponding ways.
map<Set,int>::iterator it;
map<Set,int>xs;//\sum a_i and its calc times.
void add(int &a,int b){a=(a+b)%mod;}
int upd(int a){return a>=mod?a-mod:a;}
int main()
{
scanf("%d%d",&n,&m);
for(int i=pw[0]=1;i<=60;i++) pw[i]=pw[i-1]*17;
for(int i=1;i<=m;i++) scanf("%d",&a[i]),b[a[i]]++;
sort(a+1,a+1+m);
fac[0]=fac[1]=inv[0]=inv[1]=1;
for(int i=2;i<=m;i++) fac[i]=1ll*fac[i-1]*i%mod,inv[i]=1ll*(mod-mod/i)*inv[mod%i]%mod;
for(int i=2;i<=m;i++) inv[i]=1ll*inv[i]*inv[i-1]%mod;
int now=0; dp[now][Set()]=1;
for(int i=1;i<=m;i++,now=!now){
dp[!now].clear();
for(it=dp[now].begin();it!=dp[now].end();++it){
static Set tmp; int w = it->se;
for(int j=0;j<30&&(j==0||(it->fi).a[j-1].fi);j++){
tmp=it->fi, tmp.a[j].fi+=a[i], tmp.a[j].se++;
tmp.gethash(), add(dp[!now][tmp],w);
}
}
}
for(it=dp[now].begin();it!=dp[now].end();++it){
static Set tmp; tmp=it->fi; int w = it->se;
for(int j=0;j<30&&tmp.a[j].fi;j++) w=1ll*w*fac[tmp.a[j].se-1]%mod*(tmp.a[j].se&1?1:mod-1)%mod,tmp.a[j].se=0;
tmp.gethash(), add(xs[tmp],w);
}
for(int i=2;i<=n;i++) if(!vis[i]){
++cnt; for(int j=i+i;j<=n;j+=i) vis[j]=1;
for(int x=i;x<=n;x*=i) e[cnt]+=n/x;
}
int ans=0,N=e[1];
for(it=xs.begin();it!=xs.end();++it){
num=0; for(int i=0;i<30&&(it->fi).a[i].fi;i++) A[++num]=it->fi.a[i].fi;
memset(f,0,(N+1)<<2),f[0]=1;
for(int i=1;i<=num;i++)
for(int j=A[i];j<=N;j++)
f[j]=upd(f[j]+f[j-A[i]]);
int ret=1;
for(int i=1;i<=cnt;i++) ret=1ll*ret*f[e[i]]%mod;
ans=(ans+1ll*ret*it->se)%mod;
}
for(int i=1;i<=30;i++) ans=1ll*ans*inv[b[i]]%mod;
printf("%d\n",ans);
}