題解
花了一上午+一中午終於把這道題A了
首先,我們要求的是bi互不相同的合法方案數
我們可以枚舉一個a的集合S,來強制裏面的b全部都相同,然後其它的隨便放
由於這個題的n的約數非常多,我們可以把它質因數分解一下再來做
那麼質因數分解之後怎麼來算貢獻呢?
我們可以強制每一個質因子都在S中分配相同的冪次
由於題目中的a在相等的情況下是無序的,而在不等的情況下又是有序的
所以爲了簡化問題,我們乾脆把ai都看作是不同的
這樣我們枚舉集合就可以變成O(2^m)了(好像複雜度更高了。。。)
假設我們所有的質因子的可以隨意的分配(什麼都不考慮)
那麼這樣的方案數如果用生成函數來表示就是
(最後的[x^{e_j}]表示取x^{e_j}的係數)
顯然這個生成函數是可以利用完全揹包來計算的(m個物品,每個物品大小爲ai,求花費揹包空間爲ej的方案數)
如果我們強制了一個ai的集合S必須選同樣b
那麼計算答案的式子就是(爲了簡便,我們把生成函數寫成閉形式)
(相當於把S強制捆綁成一個大物品,物品大小爲\sum ai)
然後我們自然而然地在外面套上了一個容斥係數(-1)^{|S|-1}
以爲這樣就可以拿到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)
那麼我們也可以寫出答案的式子
然後又自然而然地乘上一個(-1)^{|P|-1}的容斥係數?
還是過不了樣例啊
我們覺得這個容斥係數很煩人,於是先設它爲F[P],把它放一邊
略加思考
略加思考
略加思考
深入思考
胡亂思考
這個F[P]好像並不好算啊
那我們自己來構造一個F[P]吧
我們想一想,什麼時候答案纔可以+1呢?
很顯然,所有集合的大小都爲1的時候
那麼如果P中存在一個集合Si大小大於1,則我們要它的貢獻爲0
(這裏我們似乎發現了容斥的本質:通過對所有貢獻的加權來獲取我們需要的貢獻)
於是我們可以定義容斥係數F[P]
這樣就完了???
當然沒有
一個集合S可能會被計算多次,我們需要保證的是S在累加完所有的貢獻之後的係數爲[n=1]
即[n=1]=f[n]+(???*f[???]+??*f[??]+???)
我們可以枚舉一下S中1號點的所屬集合的大小來計算後面的一堆問號
(後面的[n-i=1]表示子問題n-i的係數)
(關於定1號點已經是組合數學的老套路了)
所以最終的式子就是
推導一下(令i=n-1)
所以f[1]=1,f[n]=-(n-1)*(-(n-2))*(-(n-3))*...*(-1)=(-1)^(n-1)*(n-1)!
最終我們完成了容斥係數的構造
所以最終的答案就是
考慮一下,我們可以枚舉每一個劃分來計算答案
然而劃分方案太多了,我們需要簡化一下
我們發現對於一個劃分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^)/~