20200515省選模擬賽B、幻化成風(毒瘤容斥題+構造容斥係數+生成函數+hash狀壓DP+Trie樹優化揹包)

image

image

 

 

題解

花了一上午+一中午終於把這道題A了

 

首先,我們要求的是bi互不相同的合法方案數

我們可以枚舉一個a的集合S,來強制裏面的b全部都相同,然後其它的隨便放

由於這個題的n的約數非常多,我們可以把它質因數分解一下再來做

n!=\prod_{i=1}^{tot}p_i^{e_i}

那麼質因數分解之後怎麼來算貢獻呢?

我們可以強制每一個質因子都在S中分配相同的冪次

由於題目中的a在相等的情況下是無序的,而在不等的情況下又是有序的

所以爲了簡化問題,我們乾脆把ai都看作是不同的

這樣我們枚舉集合就可以變成O(2^m)了(好像複雜度更高了。。。)

 

假設我們所有的質因子的可以隨意的分配(什麼都不考慮)

那麼這樣的方案數如果用生成函數來表示就是

\prod_{j=1}^{tot}(\prod_{i=1}^m{(1+x^{a_i}+x^{2*a_i}+...)})[x^{e_j}](最後的[x^{e_j}]表示取x^{e_j}的係數)

顯然這個生成函數是可以利用完全揹包來計算的(m個物品,每個物品大小爲ai,求花費揹包空間爲ej的方案數)

如果我們強制了一個ai的集合S必須選同樣b

那麼計算答案的式子就是(爲了簡便,我們把生成函數寫成閉形式)

\prod_{j=1}^{tot}(\frac{1}{1-x^{\sum_{i\in S}a_i}}*\prod_{i\notin S}\frac{1}{1-x^{a_i}})[x^{e_j}]

(相當於把S強制捆綁成一個大物品,物品大小爲\sum ai)

然後我們自然而然地在外面套上了一個容斥係數(-1)^{|S|-1}

\sum_{S}(-1)^{|S|-1}\prod_{j=1}^{tot}(\frac{1}{1-x^{\sum_{i\in S}a_i}}*\prod_{i\notin S}\frac{1}{1-x^{a_i}})[x^{e_j}]

以爲這樣就可以拿到60分了

然而發現這樣過不了樣例。。。

因爲我們容斥的時候只關注了bi互不相同,而這種情況

2 2 2 2   ({}中的元素表示該元素位於S中,裏邊的數表示集合中的ai,注意,ai相等但是位置不同我們也把它視爲不同S)

按照我們的計算方法

+{2} 2 2 2+2 {2} 2 2+2 2 {2} 2+2 2 2 {2}

- {2 2} 2 2 - {2} 2 {2} 2 - {2} 2 2 {2} - 2 {2 2} 2 - 2 {2} 2 {2} - 2 2 {2 2}

+{2 2 2} 2 +{2 2} 2 {2}+{2} 2 {2 2}+2 {2 2 2}

-{2 2 2 2}

我們把S強制捆綁成一個大物品後,計算的答案就是(用物品大小的可重集[ ]來代表揹包的結果)

假設tot=1,p1=2,e1=8

+[2 2 2 2]*4=35*4

-[4 2 2]*6=-9*6

+[6 2]*4=2*4

-[8]*1=-1

=93

而原問題的答案顯然是0(即怎麼分配2都會出現bi相等)

到底是哪裏多算呢了?

我們發現,枚舉的S集合(這其實是一個可重集),雖說內部的ai編號是不同的,但是S本身是可能被重複枚舉到的

也就是說,我們把相同構成的可重集S多算了一遍

這道題其實還需要對可重集S進行容斥

 

我們枚舉一個對a的集合劃分P={S1,S2,...S_{|P|}}(這裏就相當於在枚舉一個集合的集合,只不過這些集合之間無交集,且他們的並集爲全集a)

那麼我們也可以寫出答案的式子

\prod_{j=1}^{tot}(\prod_{i=1}^{|P|}\frac{1}{1-x^{\sum_{r\in S_i}a_r}})[x^{t_j}]

然後又自然而然地乘上一個(-1)^{|P|-1}的容斥係數?

\sum_{P}(-1)^{|P|-1}\prod_{j=1}^{tot}(\prod_{i=1}^{|P|}\frac{1}{1-x^{\sum_{r\in S_i}a_r}})[x^{t_j}]

還是過不了樣例啊

我們覺得這個容斥係數很煩人,於是先設它爲F[P],把它放一邊

\sum_{P}F[P]\prod_{j=1}^{tot}(\prod_{i=1}^{|P|}\frac{1}{1-x^{\sum_{r\in S_i}a_r}})[x^{t_j}]

略加思考

略加思考

略加思考

深入思考

胡亂思考

這個F[P]好像並不好算啊

那我們自己來構造一個F[P]吧

我們想一想,什麼時候答案纔可以+1呢?

很顯然,所有集合的大小都爲1的時候

那麼如果P中存在一個集合Si大小大於1,則我們要它的貢獻爲0

(這裏我們似乎發現了容斥的本質:通過對所有貢獻的加權來獲取我們需要的貢獻)

於是我們可以定義容斥係數F[P]

F[P]=\prod_{i=1}^{|P|}f[S_i]

f[n]=[n=1]

這樣就完了???

當然沒有

一個集合S可能會被計算多次,我們需要保證的是S在累加完所有的貢獻之後的係數爲[n=1]

即[n=1]=f[n]+(???*f[???]+??*f[??]+???)

我們可以枚舉一下S中1號點的所屬集合的大小來計算後面的一堆問號

\sum_{i=1}^{n}C(n-1,i-1)*f[i]*[n-i=1](後面的[n-i=1]表示子問題n-i的係數)

(關於定1號點已經是組合數學的老套路了)

所以最終的式子就是

[n=1]=f[n]+\sum_{i=1}^{n}C(n-1,i-1)*f[i]*[n-i=1]

推導一下(令i=n-1)

f[n]=[n=1]-(n-1)*f[n-1]

所以f[1]=1,f[n]=-(n-1)*(-(n-2))*(-(n-3))*...*(-1)=(-1)^(n-1)*(n-1)!

最終我們完成了容斥係數的構造

f[n]=(-1)^{n-1}*(n-1)!

所以最終的答案就是

\sum_P(\prod_{i=1}^{|P|}f[|S_i|])*\prod_{j=1}^{tot}(\prod_{i=1}^{|P|}\frac{1}{1-x^{\sum_{r\in S_i}a_r}})[x^{t_j}]

 

考慮一下,我們可以枚舉每一個劃分來計算答案

然而劃分方案太多了,我們需要簡化一下

我們發現對於一個劃分P={S1,S2,...,Sk}

我們用到的只有每個集合Si的ai之和,以及每個集合的大小|Si|,把這些ai之和拿來做揹包,把Si拿來計算容斥係數

(這裏的意思是把sumai與Si的組成相同的P都壓縮到一起來求解,最後乘上組成這種P的方案數即可)

(我們可以用結構體+hash來表示一個劃分P,利用map來存一個結構體的DP值)

(整個程序就分爲四個部分:

1、預處理

2、枚舉劃分P,並把可以一起計算的劃分壓縮在一起,構成一個壓縮劃分集

3、求出每一組壓縮劃分集的容斥係數(由於Si是一樣的,所以容斥係數也相等),並與該壓縮劃分集的大小相乘

4、對每一組壓縮劃分集求出揹包數組,並算出每個質因子的答案,與它所帶的係數相乘,求和就是答案)

我們還可以稍微優化第四步:

把sumai相同的壓縮劃分集再壓縮一次,構成一個二次壓縮劃分集(可以用multiset來存)

實測壓縮劃分集最壞大概有57000左右,二次壓縮劃分集最多隻有[30的整數劃分=5604]個

所以我們的平均加入的物品個數爲O(5604*E(|P|))

每一個物品我們都要花O(maxtj)的複雜度加入揹包

所以總複雜度是O(5604*E(|P|)maxtj)

實測E(|P|)≈10.3,maxtj=9995

T到飛起

我們稍微運用一下ctime,發現大部分時間都花在了揹包上,而揹包的計算次數高達5億次!!

怎麼辦呢

開O2:3.4s  >>  2.8s

加法取模優化:2.8s  >>  2.67s

循環展開:2.67s  >>  2.55s(爲什麼只優化了這麼點兒啊。。。)

開小結構體的內部數組:TLE >> WA(不知道哪裏出了問題。。。估計成功了也沒有什麼大優化。。。)

 

後來中午翹掉午休到機房繼續肝這道題,想到了一個奇妙的做法

我把每一個二次壓縮劃分集的物品大小輸出了出來

發現有很多物品是重複的

好像我們有很多重複的計算的呢。。

如果我們把物品大小序列看成一個字符串,把它們插入到一棵Trie樹中,dfs一遍,每走一條邊就加入一個物品

那麼一些前綴相同的字符串的可以只計算一次該前綴

這樣就省去了不少重複加入的物品

由於一個二次壓縮劃分集最多隻有30個物品

我們可以對每一個深度開一個DP,每次更新的時候memcpy之前的DP狀態就可以了

這樣做的物品數大概爲12000+,時間複雜度爲12000*10000左右,2s+O2是可以通過的

代碼1:(TLE,註釋滿滿的代碼)

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<map>
#include<set>
using namespace std;
#define M 10005
#define N 32
#define LL unsigned long long
#define fi first
#define se second
const int mod=1000000007;
bool vis[M];
int prime[M],tot,fac[N],finv[N];
int cnt[M];//mei zhong zhi yin zi de chu xian ci shu
int ksm(int x,int y)
{
	int ret=1;
	while(y){
		if(y&1)ret=1ll*ret*x%mod;
		y>>=1;x=1ll*x*x%mod;
	}
	return ret;
}
void shai(int n)
{
	int i,j;vis[1]=1;
	for(i=2;i<=n;i++){
		if(!vis[i])prime[++tot]=i;
		for(j=1;j<=tot;j++){
			int tmp=i*prime[j];if(tmp>n)break;
			vis[tmp]=1;if(i%prime[j]==0)break;
		}
	}
	for(i=1;i<=tot;i++)
		for(j=prime[i];j<=n;j*=prime[i])
			cnt[i]+=n/j;
	fac[0]=fac[1]=finv[0]=finv[1]=1;
	for(i=2;i<=30;i++)fac[i]=1ll*fac[i-1]*i%mod;
	finv[30]=ksm(fac[30],mod-2);
	for(i=30;i>=1;i--)finv[i-1]=1ll*finv[i]*i%mod;
}
int a[N],tong[N];//tong:mei zhong a de chu xian ci shu(yong lai zui hou /fac[tong[i]])
struct P{
	//ya suo hou de hua fen:ba suma he size qu zhi xiang tong de hua fen fang dao yi qi
	int len;LL hh1,hh2;
	pair<int,int> a[32];//fi:suma,se:size
	P(){len=0;hh1=0;memset(a,0,sizeof(a));}
	void gethh(){
		sort(a+1,a+len+1);hh1=0;
		for(int i=1;i<=len;i++)//{
			hh1=hh1*3993991+(LL)a[i].fi+(LL)a[i].se*1997;
			//hh2=hh2;
		//}
	}
	bool operator < (const P &t)const{
		//if(len!=t.len)return len<t.len;
		return hh1<t.hh1;
		//if(hh2!=t.hh2)return hh2<t.hh2;
		//for(int i=1;i<=len;i++){
		//	if(a[i].fi!=t.a[i].fi)return a[i].fi<t.a[i].fi;
		//	if(a[i].se!=t.a[i].se)return a[i].se<t.a[i].se;
		//}
		//return 0;
	}
}pa,pb;
map<P,int> mp[2];//tong ji mei zhong ya suo hua fen de chu xian ci shu
map<P,int>::iterator it;
multiset<int> S;//ba suma xiang tong de zai dan du ya suo yi xia
//zhe yang ke yi jia kuai bei bao d ci shu
multiset<int>::iterator its;
map<multiset<int>,int> F;//hua fen de rcxs deng yu ji he de rcxs zhi ji
//F[P]=\prod (-1)^(|Si|-1)*(|Si|-1)!
map<multiset<int>,int>::iterator itf;
int rc(int x){return x&1?mod-1:1;}
int f[M];//zuo wan quan bei bao:O(n*m)
//hao xiang ke yi duo xiang shi qiu ni zai juan ji zuo dao O(m*nlogn)---->sb 

#include<ctime>
int main()
{
	int n,m,i,j;
	scanf("%d%d",&n,&m);
	for(i=1;i<=m;i++){scanf("%d",&a[i]);tong[a[i]]++;}
	shai(n);sort(a+1,a+m+1);
	int now=0;
	mp[now][pa]=1;
	
	double c1=clock();
	
	for(i=1;i<=m;i++){
		mp[now^1].clear();
		for(it=mp[now].begin();it!=mp[now].end();it++){
			pa=(*it).fi;
			for(j=1;j<=pa.len+1;j++){
				pb=pa;
				pb.a[j].fi+=a[i];pb.a[j].se++;
				if(j==pa.len+1)pb.len++;
				pb.gethh();
				mp[now^1][pb]=(mp[now^1][pb]+(*it).se)%mod;
			}
		}
		now^=1;
	}
	
	printf("%d:%.3f\n",mp[now].size(),(clock()-c1)/1000);
	
	//int ttmp[55]={0},tcnt=0;
	for(it=mp[now].begin();it!=mp[now].end();it++){
		pa=(*it).fi;int mulf=(*it).se;
		//ttmp[++tcnt]=mulf;
		S.clear();
		for(i=1;i<=pa.len;i++){
			S.insert(pa.a[i].fi);
			mulf=1ll*mulf*rc(pa.a[i].se-1)%mod*fac[pa.a[i].se-1]%mod;
		}
		F[S]=(F[S]+mulf)%mod;//lei jia deng xiao ji he de rcxs
		//printf("%d\n",F[S]);
	}
	
	printf("%d:%.3f\n",F.size(),(clock()-c1)/1000);
	//int T = 0;
	int ans=0;//ct=0;
	for(itf=F.begin();itf!=F.end();itf++){
		//ct++;
		S=(*itf).fi;
		//ttmp[++tcnt]=(*itf).se;
		memset(f,0,sizeof(f));f[0]=1;
		for(its=S.begin();its!=S.end();its++){
			int v=*its;
			for(i=v;i<=cnt[1];i++){
				f[i]+=f[i-v];
				if(f[i]>=mod)f[i]-=mod;
			}
			//T+=cnt[1]-v;
		}
		int mulf=1;
		for(i=1;i<=tot;i++)
			mulf=1ll*mulf*f[cnt[i]]%mod;
		ans=(1ll*ans+1ll*(*itf).se*mulf)%mod;
		//printf("%d: %d\n",ct,ans);
	}
	//printf("-- %d \n",T);
	//sort(ttmp+1,ttmp+tcnt+1);
	//for(i=1;i<=tcnt;i++)
	//	printf("%d\n",ttmp[i]);
	
	for(i=1;i<=30;i++)if(tong[i])
		ans=1ll*ans*finv[tong[i]]%mod;
	printf("%d\n",ans);
	
	printf("%.3f\n",(clock()-c1)/1000);
}

 

代碼2:(TLE,加入了無數卡常技巧的代碼)

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<map>
#include<set>
using namespace std;
#define M 10005
#define N 31
#define LL unsigned long long
#define fi first
#define se second
const int mod=1000000007;
bool vis[M];
int prime[M],tot,fac[N],finv[N];
int cnt[M];
int ksm(int x,int y)
{
	int ret=1;
	while(y){
		if(y&1)ret=1ll*ret*x%mod;
		y>>=1;x=1ll*x*x%mod;
	}
	return ret;
}
void shai(int n)
{
	int i,j;vis[1]=1;
	for(i=2;i<=n;i++){
		if(!vis[i])prime[++tot]=i;
		for(j=1;j<=tot;j++){
			int tmp=i*prime[j];if(tmp>n)break;
			vis[tmp]=1;if(i%prime[j]==0)break;
		}
	}
	for(i=1;i<=tot;i++)
		for(j=prime[i];j<=n;j*=prime[i])
			cnt[i]+=n/j;
	fac[0]=fac[1]=finv[0]=finv[1]=1;
	for(i=2;i<=30;i++)fac[i]=1ll*fac[i-1]*i%mod;
	finv[30]=ksm(fac[30],mod-2);
	for(i=30;i>=1;i--)finv[i-1]=1ll*finv[i]*i%mod;
}
int a[N],tong[N];
struct P{
	int len;LL hh1,hh2;
	pair<int,int> a[N];
	P(){len=0;hh1=0;memset(a,0,sizeof(a));}
	void gethh(){
		sort(a+1,a+len+1);hh1=0;
		for(int i=1;i<=len;i++)
			hh1=hh1*3993991+a[i].fi+1997ll*a[i].se;
	}
	bool operator < (const P &t)const{return hh1<t.hh1;}
}pa,pb;
map<P,int> mp[2];
map<P,int>::iterator it;
multiset<int> S;
multiset<int>::iterator its;
map<multiset<int>,int> F;
map<multiset<int>,int>::iterator itf;
//int rc(int x){return x&1?mod-1:1;}
//int f[M];

//#include<ctime>
int ans;
struct trie{
	int ch[31],k;
}tr[100005];
int trtot,rt;
int tmp[31],tcnt;
void insert(int &i,int pos,int k)
{
	if(!i)i=++trtot;
	if(pos>tcnt){
		tr[i].k=k;
		return;
	}
	insert(tr[i].ch[tmp[pos]],pos+1,k);
}
int f[32][M];
void BAG(int i,int dep)
{
	if(tr[i].k){
		int mulf=1;
		for(int j=1;j<=tot;j++)
			mulf=1ll*mulf*f[dep-1][cnt[j]]%mod;
		ans=(1ll*ans+1ll*tr[i].k*mulf)%mod;
		return;
	}
	for(int j=1;j<=30;j++){
		if(!tr[i].ch[j])continue;
		memcpy(f[dep],f[dep-1],sizeof(f[dep-1]));
		for(int k=j;k<=cnt[1];k++){
			f[dep][k]+=f[dep][k-j];
			if(f[dep][k]>=mod)f[dep][k]-=mod;
		}
		BAG(tr[i].ch[j],dep+1);
	}
}
int main()
{
	//freopen("B.out","w",stdout);
	int n,m,i,j;
	scanf("%d%d",&n,&m);
	for(i=1;i<=m;i++){scanf("%d",&a[i]);tong[a[i]]++;}
	shai(n);sort(a+1,a+m+1);
	int now=0;
	mp[now][pa]=1;
	
	//double c1=clock();
	
	for(i=1;i<=m;i++){
		mp[now^1].clear();
		for(it=mp[now].begin();it!=mp[now].end();it++){
			pa=(*it).fi;
			for(j=1;j<=pa.len+1;j++){
				pb=pa;
				pb.a[j].fi+=a[i];pb.a[j].se++;
				if(j==pa.len+1)pb.len++;
				pb.gethh();
				mp[now^1][pb]=(mp[now^1][pb]+(*it).se)%mod;
			}
		}
		now^=1;
	}
	
	//printf("%d:%.3f\n",mp[now].size(),(clock()-c1)/1000);
	
	for(it=mp[now].begin();it!=mp[now].end();it++){
		pa=(*it).fi;int mulf=(*it).se;
		S.clear();
		for(i=1;i<=pa.len;i++){
			S.insert(pa.a[i].fi);
			mulf=1ll*mulf*fac[pa.a[i].se-1]%mod;
			if(!(pa.a[i].se&1))mulf=1ll*mulf*(mod-1)%mod;
		}
		F[S]=(F[S]+mulf)%mod;
	}
	
	//printf("%d:%.3f\n",F.size(),(clock()-c1)/1000);
	//double T1=clock(),T2=0;
	rt=0;trtot=0;
	for(itf=F.begin();itf!=F.end();itf++){
		S=(*itf).fi;tcnt=0;
		for(its=S.begin();its!=S.end();its++)
			tmp[++tcnt]=*its;
		insert(rt,1,(*itf).se);
	}
	f[0][0]=1;
	BAG(rt,1);
	/*int ans=0;int con=0;
	for(itf=F.begin();itf!=F.end();itf++){
		S=(*itf).fi;
		memset(f,0,sizeof(f));f[0]=1;
		for(its=S.begin();its!=S.end();its++){
			int v=*its;
			double cd=clock();
			printf("%d ",v);
			//con++;
			for(i=v;i<=cnt[1];i++){
				int t=i-v;
				f[i]+=f[t];if(f[i]>=mod)f[i]-=mod;
				if(i+8<=cnt[1]){
					f[i+1]+=f[t+1];if(f[i+1]>=mod)f[i+1]-=mod;
					f[i+2]+=f[t+2];if(f[i+2]>=mod)f[i+2]-=mod;
					f[i+3]+=f[t+3];if(f[i+3]>=mod)f[i+3]-=mod;
					f[i+4]+=f[t+4];if(f[i+4]>=mod)f[i+4]-=mod;
					f[i+5]+=f[t+5];if(f[i+5]>=mod)f[i+5]-=mod;
					f[i+6]+=f[t+6];if(f[i+6]>=mod)f[i+6]-=mod;
					f[i+7]+=f[t+7];if(f[i+7]>=mod)f[i+7]-=mod;
					f[i+8]+=f[t+8];if(f[i+8]>=mod)f[i+8]-=mod;
					i+=8;
				}
			}
			
			T2+=clock()-cd;
		}
		printf("\n");
		int mulf=1;
		for(i=1;i<=tot;i++)
			mulf=1ll*mulf*f[cnt[i]]%mod;
		ans=(1ll*ans+1ll*(*itf).se*mulf)%mod;
	}*/
	
	for(i=1;i<=30;i++)if(tong[i])
		ans=1ll*ans*finv[tong[i]]%mod;
	printf("%d\n",ans);
	
	//printf("%d\n",con);
	//printf("%.3f\n",(clock()-c1)/1000);
	//printf("other:%.3f\nbag:%.3f\n",(clock()-T1-T2)/1000,T2/2);
}

 

代碼3:(AC 總時間:6000+ms最快代碼,並刪掉了許多註釋)

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<map>
#include<set>
using namespace std;
#define M 10005
#define N 31
#define LL unsigned long long
#define fi first
#define se second
const int mod=1000000007;
bool vis[M];
int prime[M],tot,fac[N],finv[N];
int cnt[M];
int ksm(int x,int y)
{
	int ret=1;
	while(y){
		if(y&1)ret=1ll*ret*x%mod;
		y>>=1;x=1ll*x*x%mod;
	}
	return ret;
}
void shai(int n)
{
	int i,j;vis[1]=1;
	for(i=2;i<=n;i++){
		if(!vis[i])prime[++tot]=i;
		for(j=1;j<=tot;j++){
			int tmp=i*prime[j];if(tmp>n)break;
			vis[tmp]=1;if(i%prime[j]==0)break;
		}
	}
	for(i=1;i<=tot;i++)
		for(j=prime[i];j<=n;j*=prime[i])
			cnt[i]+=n/j;
	fac[0]=fac[1]=finv[0]=finv[1]=1;
	for(i=2;i<=30;i++)fac[i]=1ll*fac[i-1]*i%mod;
	finv[30]=ksm(fac[30],mod-2);
	for(i=30;i>=1;i--)finv[i-1]=1ll*finv[i]*i%mod;
}
int a[N],tong[N];
struct P{
	int len;LL hh1,hh2;
	pair<int,int> a[N];
	P(){len=0;hh1=0;memset(a,0,sizeof(a));}
	void gethh(){
		sort(a+1,a+len+1);hh1=0;
		for(int i=1;i<=len;i++)
			hh1=hh1*3993991+a[i].fi+1997ll*a[i].se;
	}
	bool operator < (const P &t)const{return hh1<t.hh1;}
}pa,pb;
map<P,int> mp[2];
map<P,int>::iterator it;
multiset<int> S;
multiset<int>::iterator its;
map<multiset<int>,int> F;
map<multiset<int>,int>::iterator itf;
int ans;
struct trie{
	int ch[31],k;
}tr[60005];
int trtot,rt;
int tmp[31],tcnt;
void insert(int &i,int pos,int k)
{
	if(!i)i=++trtot;
	if(pos>tcnt){tr[i].k=k;return;}
	insert(tr[i].ch[tmp[pos]],pos+1,k);
}
int f[32][M];
void BAG(int i,int dep)
{
	if(tr[i].k){
		int mulf=1;
		for(int j=1;j<=tot;j++)
			mulf=1ll*mulf*f[dep-1][cnt[j]]%mod;
		ans=(1ll*ans+1ll*tr[i].k*mulf)%mod;
		return;
	}
	for(int j=1;j<=30;j++){
		if(!tr[i].ch[j])continue;
		memcpy(f[dep],f[dep-1],sizeof(f[dep-1]));
		for(int k=j;k<=cnt[1];k++){
			f[dep][k]+=f[dep][k-j];
			if(f[dep][k]>=mod)f[dep][k]-=mod;
		}
		BAG(tr[i].ch[j],dep+1);
	}
}
int main()
{
	int n,m,i,j;
	scanf("%d%d",&n,&m);
	for(i=1;i<=m;i++){scanf("%d",&a[i]);tong[a[i]]++;}
	shai(n);sort(a+1,a+m+1);
	int now=0;
	mp[now][pa]=1;
	for(i=1;i<=m;i++){
		mp[now^1].clear();
		for(it=mp[now].begin();it!=mp[now].end();it++){
			pa=(*it).fi;
			for(j=1;j<=pa.len+1;j++){
				pb=pa;
				pb.a[j].fi+=a[i];pb.a[j].se++;
				if(j==pa.len+1)pb.len++;
				pb.gethh();
				mp[now^1][pb]=(mp[now^1][pb]+(*it).se)%mod;
			}
		}
		now^=1;
	}
	for(it=mp[now].begin();it!=mp[now].end();it++){
		pa=(*it).fi;int mulf=(*it).se;
		S.clear();
		for(i=1;i<=pa.len;i++){
			S.insert(pa.a[i].fi);
			mulf=1ll*mulf*fac[pa.a[i].se-1]%mod;
			if(!(pa.a[i].se&1))mulf=1ll*mulf*(mod-1)%mod;
		}
		F[S]=(F[S]+mulf)%mod;
	}
	rt=0;trtot=0;
	for(itf=F.begin();itf!=F.end();itf++){
		S=(*itf).fi;tcnt=0;
		for(its=S.begin();its!=S.end();its++)
			tmp[++tcnt]=*its;
		insert(rt,1,(*itf).se);
	}
	f[0][0]=1;
	BAG(rt,1);
	for(i=1;i<=30;i++)if(tong[i])
		ans=1ll*ans*finv[tong[i]]%mod;
	printf("%d\n",ans);
}

 

\(^o^)/~\(^o^)/~\(^o^)/~完結撒花\(^o^)/~\(^o^)/~\(^o^)/~

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章