Loading

P4495 [HAOI2018]奇怪的背包

题意

给你 \(n\) 个数,每次询问给出一个 \(w\),求有多少种选数的方式,使得每种数可以重复使用的情况下,使得加和在 \(p\) 的剩余系下为 \(w\)

Solution

首先考虑判断一个数 \(v\) 是否可行,这样的话相当于要解决:

\[xv\equiv w\pmod p \]

可以套路地转化成方程组来求解:

\[vx+py=w \]

但是我们只需要判断有没有解,所以只要判断:\(\gcd(v,p)\) 是否是 \(w\) 的因子。

那如果有很多数 \(v_1,v_2,\cdots,v_k\) 呢?可以大胆猜测,充要条件就是:\(\gcd(v_1\sim v_k,p)\)\(w\) 的因子。

于是问题转化成有多少种选取子集的方式,使得子集元素的 \(\gcd\)\(p\) 的最大公约数是 \(w\) 的因子。

\(\color{red}{\bigstar}\) 然后直接考虑 \(dp_{i,j}\) 表示考虑完前 \(i\) 个物品后与 \(p\)\(\gcd\)\(j\) 的方案数。为了优化,我们可以把每个 \(a_i\)\(p\) 先取一次 \(\gcd\),然后相同的压缩起来,这样总点数不超过 \(p\) 的因子个数即 \(p^{\frac{1}{3}}\)。那我们假设第 \(i\) 种数有 \(s_i\) 个,那么转移就是:

\[dp_{i,j}=dp_{i-1,j}+dp_{i-1,k}\times (2^{s_i}-1) \]

这样转移不是很方便,可以采用刷板法。

由于 \(j\) 的取值只有 \(p\) 的因子个,所以 dp 变成了 \(O(\sigma(p)^2)\)

然后考虑求答案,那么应该是:

\[\sum_{i|w} dp_{n,i} \]

为了减小 \(w\) 的规模,我们对它也和 \(p\) 做一次 \(\gcd\)。所以变成了:

\[\sum_{i|\gcd(w,p)} dp_{n,i} \]

其实是一样的,只不过这样的话,我们就不需要每次询问都枚举,因为 \(\gcd(w,p)\) 的因子一定还是 \(p\) 的因子,所以我们只需要在这 \(\sigma(p)\) 个数之间预处理答案就可以了,还是 \(O(\sigma(p)^2)\)

Code

// Problem: P4495 [HAOI2018]奇怪的背包
// Contest: Luogu
// URL: https://www.luogu.com.cn/problem/P4495
// Memory Limit: 500 MB
// Time Limit: 2000 ms

#include<bits/stdc++.h>
// #define ll long long
#define inf (1<<30)
#define INF (1ll<<60)
#define pb emplace_back
#define pii pair<int,int>
#define mkp make_pair
#define fi first
#define se second
#define all(a) a.begin(),a.end()
#define rep(i,j,k) for(int i=(j);i<=(k);i++)
#define per(i,j,k) for(int i=(j);i>=(k);i--)
#define pt(a) cerr<<#a<<'='<<a<<' '
#define pts(a) cerr<<#a<<'='<<a<<'\n'
#define int long long
using namespace std;
const int MAXN=2e3+10;
const int MOD=1e9+7;
int ksm(int a,int p){
	int ret=1;while(p){
		if(p&1) ret=ret*a%MOD;
		a=a*a%MOD; p>>=1;
	}return ret;
}
int gcd(int x,int y){return !y?x:gcd(y,x%y);}
map<int,int> g;
int lsh[MAXN*MAXN],a[MAXN],s[MAXN],tot,d[MAXN],cnt;
int dp[MAXN][MAXN],ans[MAXN];
signed main()
{
	ios::sync_with_stdio(0);
	cin.tie(0);cout.tie(0);
	int n,q,p;
	cin>>n>>q>>p;
	rep(i,1,n) cin>>lsh[i],lsh[i]=gcd(lsh[i],p);
	sort(lsh+1,lsh+1+n);
	rep(i,1,n) if(lsh[i]!=lsh[i-1]){a[++tot]=lsh[i],s[tot]=1;}else s[tot]++;
	for(int i=1;i*i<=p;i++) if(p%i==0){
		d[++cnt]=i;if(i*i!=p) d[++cnt]=p/i;
	}
	sort(d+1,d+1+cnt);
	rep(i,1,cnt) g[d[i]]=i;
	dp[0][cnt]=1;
	rep(i,1,tot) rep(j,1,cnt){
		int k=g[gcd(a[i],d[j])];
		(dp[i][j]+=dp[i-1][j])%=MOD;
		(dp[i][k]+=dp[i-1][j]*(ksm(2,s[i])+MOD-1)%MOD)%=MOD;
	}
	rep(i,1,cnt) rep(j,1,i) if(d[i]%d[j]==0) ans[i]=(ans[i]+dp[tot][j])%MOD;
	while(q--){
		int w;cin>>w;
		cout<<ans[g[gcd(w,p)]]<<'\n';
	}
	return 0;
}
posted @ 2022-07-29 17:02  ZCETHAN  阅读(26)  评论(0编辑  收藏  举报