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

题意简述

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

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

(n,qleq 10^6) (Pleq 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
",ans[mp[w]]);
	}
	
	return 0;
}
原文地址:https://www.cnblogs.com/lindalee/p/12676502.html