1. 程式人生 > >[HAOI2018] 奇怪的背包

[HAOI2018] 奇怪的背包

count -s class 相同 數論 我們 def DC HA

技術分享圖片

技術分享圖片

套著dp外套的數論題hhhhh。

不難推出以下的幾個性質:

1.第i種物品能表示的數 -> gcd(V[i] , P) 的倍數。

2.查詢W[i] 其實就相當於查詢 gcd(W[i] , P).

3.在%P同余系下 ,ax + by 可以表示的數 與 gcd(a,b) 的倍數 是相同的集合。

看起來有點突兀,但實際特別好證明,第1條其實就是個同余方程的應用;而第2條是因為,所有能被表示的數肯定都是P的約數,所以W[i]只有與P的公約數會對答案有貢獻;第三條也是同余基本定理嘛qwq

然後發現P的約數在10^9以下最多有10^3個,所以實際的物品和查詢數也只有10^3級別了hhhh(很多物品與P的gcd一樣的時候隨便取一個非空子集就相當於選了這種gcd的物品)

我們就可以先開開心心dp 出 f[i][j] 表示考慮了前i種物品,且目前的gcd是j的方案數,最後對於P的每個約數k,處理出它可以被表示的方案數(也就是Σf[last][j] * [j|k])。

以上兩個部分都是 O(P約數個數 ^2) 的,輕輕松松過本題 (由於偷懶我用了STL的hashmap,還差點被卡hhhh)

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int ha=1000000007;
int gcd(int x,int y){ return y?gcd(y,x%y):x;}
inline int add(int x,int y){ x+=y; return x>=ha?x-ha:x;}
inline void ADD(int &x,int y){ x+=y; if(x>=ha) x-=ha;}
unordered_map<int,int> N,F[2],ANS;
unordered_map<int,int> ::iterator it,IT;
int n,now,T,Q,P,nxt,to;
inline int read(){
	int x=0; char ch=getchar();
	for(;!isdigit(ch);ch=getchar());
	for(;isdigit(ch);ch=getchar()) x=x*10+ch-‘0‘;
	return x;
}
inline void W(int x){ if(x>=10) W(x/10); putchar(x%10+‘0‘);}
inline void update(int x){ if(!N.count(x)) N[x]=2; else (N[x]*=2)%=ha;}

inline void dp(){
    now=0,nxt=F[0][P]=1;
    int cnt=0;
	for(it=N.begin();it!=N.end();now=nxt,nxt^=1,++it){
		F[nxt].clear(),cnt++;
		for(IT=F[now].begin();IT!=F[now].end();++IT){
			ADD(F[nxt][IT->first],IT->second);
			ADD(F[nxt][gcd(IT->first,it->first)],add(it->second,ha-1)*(ll)IT->second%ha);
		}
	}
	
	for(int i=1;i*(ll)i<=P;i++) if(!(P%i)) ANS[i]=ANS[P/i]=0;
	for(it=ANS.begin();it!=ANS.end();++it)
	    for(IT=F[now].begin();IT!=F[now].end();++IT) if(!(it->first%IT->first)) ADD(it->second,IT->second);
}

inline void solve(){ while(Q--) W(ANS[gcd(read(),P)]),puts("");}

int main(){
	scanf("%d%d%d",&n,&Q,&P);
	for(int i=1;i<=n;i++) update(gcd(read(),P));
	dp(),solve();
	return 0;
}

  

[HAOI2018] 奇怪的背包