[HAOI2018]奇怪的揹包【動態規劃】【數論】

傳送門

對於給出來的一個揹包重量w,它能表示出來的是w和給定模數gcd的所有倍數。

即gcd(mod,w),2*gcd(mod,w),3*gcd(mod,w)……

可以從循環節的角度感性理解。

所以每一個數讀入時,可以先轉化成和模數的gcd。然後能表示出所有它倍數的重量。

所以我們選出來的揹包加上模數的總gcd,應該是w的約數。這樣才能表示出它來。(反正模數可以視作一個揹包)

然而模數的約數又只有那麼一兩千個。所以我們大可以求出來,然後考慮動態規劃。(現在有兩個範圍在兩千左右的數列可以n方了)

f[i][j]表示考慮到第i揹包,和模數的gcd是j(這裏指第j個模數的約數(開個map就是了))。枚舉第i個揹包是否選擇,我們可以得到兩個轉移。一是繼承上一種,二是選擇當前數,gcd會變化,加進去(見代碼)。

爲什麼要用種呢,因爲n有一百萬,但我們只需要考慮不同的兩千個約數。對於每一個約數,可能有好多個揹包。這些揹包只要出現一個就行。那方案數就是非空子集,也就是2^{cnt}-1

所以累加的時候乘起來。最後統計答案的時候,對於一個w,所有w的倍數的f都要算進去。

然後輸出即可。

#include<bits/stdc++.h>
using namespace std;
#define in read()
#define int long long
int in{
	int cnt=0,f=1;char ch=0;
	while(!isdigit(ch)){
		ch=getchar();if(ch=='-')f=-1;
	}
	while(isdigit(ch)){
		cnt=cnt*10+ch-48;
		ch=getchar();
	}return cnt*f;
}
const int MOD=1e9+7;
int n,q,mod,v[1000003];
map<int,int> m;
int cnt[2003],id,cnt2;
int d[2003];
int jz2[1000003];
int ans[2003];
int gcd(int a,int b){
	if(!b)return a;
	return gcd(b,a%b);
}
int f[2003][2003];
signed main(){
	n=in;q=in;mod=in;jz2[0]=1;for(int i=1;i<=1000000;i++)jz2[i]=jz2[i-1]*2%MOD;
	//for(int i=1;i<=10;i++)cout<<jz2[i]<<" ";cout<<endl;
	for(int i=1;i<=n;i++)v[i]=gcd(in,mod);
	sort(v+1,v+n+1);
	for(int i=1;i<=n;i++){
		if(v[i]!=v[i-1])v[++id]=v[i],cnt[id]=1;else cnt[id]++;
	}n=id;
//	for(int i=1;i<=n;i++)cout<<v[i]<<" "<<cnt[i]<<endl;;cout<<endl;
	for(int i=1;i*i<=mod;i++){
		if(mod%i==0){
			d[++cnt2]=i;
			if(i*i!=mod)d[++cnt2]=mod/i;
		}
	}
	sort(d+1,d+cnt2+1);
	for(int i=1;i<=cnt2;i++)m[d[i]]=i;
	//for(int i=1;i<=cnt2;i++)cout<<d[i]<<" ";cout<<endl;
	f[0][cnt2]=1;
	for(int i=1;i<=n;i++){
		for(int j=1;j<=cnt2;j++){
			int k=m[gcd(d[j],v[i])];
			f[i][j]=(f[i][j]+f[i-1][j])%MOD;
			f[i][k]=(f[i][k]+f[i-1][j]*(jz2[cnt[i]]-1)%MOD)%MOD;
		}
	}
//	for(int i=1;i<=n;i++){
//		for(int j=1;j<=cnt2;j++)cout<<f[i][j]<<" ";cout<<endl;
//	}
	for(int i=1;i<=cnt2;i++){
		for(int j=1;j<=i;j++){
			if(d[i]%d[j]==0)ans[i]=(ans[i]+f[n][j])%MOD;
		}
	}
//	for(int i=1;i<=cnt2;i++)cout<<ans[i]<<" ";cout<<endl;
	for(int i=1;i<=q;i++){
		cout<<ans[m[gcd(in,mod)]]<<'\n';
	}
	return 0;
}

 

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