[loj#2523] [HAOI2018] 奇怪的背包

题意简述

有一个奇怪的背包,它有一个参数 \(P\) ,当背包内放入若干个物品后,背包的重量是物品总体积对 \(P\) 取模后的结果。

现有 \(n\) 种体积不同的物品,第 \(i\) 种占用体积为 \(V_i\) ,每种物品都有无限个。
\(q\) 次询问,每次询问给出重量 \(w_i\) ,求有多少种放入物品的方案,能将一个初始为空的背包的重量变为 \(w_i\) 。注意,两种方案被认为是不同的,当且仅当放入物品的种类不同,而与每种物品放入的个数无关。不难发现总的方案数为 \(2^n\)
结果对 998244353 取模。

\(n,q\leq 10^6\) \(P\leq 10^9\)


想法

很显然能推出要取与 \(P\)\(gcd\) 的性质。
预处理出 \(P\) 的所有因数(个数是 \(10^3\)) 级别,然后随便 \(dp\) 或容斥就好了。
有点容易想错,写之前一定要想清楚。

(我好懒)


总结

性质

一个大数 \(P\) 的因子个数是 \(P^{\frac{1}{3}}\) 级别的。


代码

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cmath>
#include<map>

#define mod 1000000007

using namespace std;

int read(){
	int x=0;
	char ch=getchar();
	while(!isdigit(ch)) ch=getchar();
	while(isdigit(ch)) x=x*10+ch-'0',ch=getchar();
	return x;
}

const int N = 1000005;

map<int,int> mp;
int n,Q,P,m,v[N],p[N],t[N],f[N],g[N],ans[N];
int gcd(int a,int b) { return b?gcd(b,a%b):a; }
int Pow_mod(int x,int y) {
	int ret=1;
	while(y){
		if(y&1) ret=1ll*ret*x%mod;
		x=1ll*x*x%mod;
		y>>=1;
	}
	return ret;
}
int Plus(int x,int y) { return x+y>=mod?x+y-mod:x+y; }
int Minus(int x,int y) { return x>=y?x-y:x-y+mod; }

int main()
{
	n=read(); Q=read(); P=read();
	for(int i=1;i<=n;i++) v[i]=gcd(P,read());
	
	for(int i=1;i*i<=P;i++) {
		if(P%i) continue;
		p[++m]=i;
	}
	for(int i=m;i>0;i--){
		if(p[i]*p[i]==P) continue;
		p[++m]=P/p[i];
	}
	for(int i=1;i<=m;i++) mp[p[i]]=i;
	for(int i=1;i<=n;i++) t[mp[v[i]]]++;
	
	for(int i=1;i<=m;i++){
		g[i]=t[i];
		for(int j=i+1;j<=m;j++)
			if(p[j]%p[i]==0) g[i]+=t[j];
		g[i]=Minus(Pow_mod(2,g[i]),1);
	}
	for(int i=m;i>0;i--){
		f[i]=g[i];
		for(int j=i+1;j<=m;j++)
			if(p[j]%p[i]==0) f[i]=Minus(f[i],f[j]);
	}
	for(int i=1;i<=m;i++){
		ans[i]=Plus(ans[i],f[i]);
		for(int j=i+1;j<=m;j++)
			if(p[j]%p[i]==0) ans[j]=Plus(ans[j],f[i]);
	}
	
	int w;
	while(Q--){
		w=gcd(read(),P);
		printf("%d\n",ans[mp[w]]);
	}
	
	return 0;
}
posted @ 2020-04-10 22:12  秋千旁的蜂蝶~  阅读(165)  评论(0编辑  收藏  举报