Luogu4495 [HAOI2018] 奇怪的背包 【扩展欧几里得算法】

题目分析:

首先打个暴力求一下$10^9$以内因子最多的数的因子个数,发现只有$1344$个。

由于有$ax+by=k*(a,b)$和2017年noip的结论,所以我们可以发现对于任意多个数$a_1,a_2,a_3,...,a_n$他们能组成的数是$k$倍的最大公约数,$k$任取。我们发现如果$gcd%p$不是$w$的因子那么不行,否则可行。所以把$a$数组全部模$p$,再归类为每个因子,再处理相互之间能构建出来的$gcd$,再用莫比乌斯函数做一下容斥,再处理出每个因子的因子和,再对每个输入的$w$模$p$,答案可以$O(1)$回答。

 

代码:

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 
 4 const int maxn = 1050000;
 5 const int MXF = 1350;
 6 const int mod = 1e9+7;
 7 
 8 int n,q,p;
 9 int fac[MXF],dt[MXF],num;
10 int a[maxn],chs[MXF],mu[MXF];
11 int pw2[maxn];
12 
13 int gcd(int a,int b){
14     if(!b) return a;
15     else return gcd(b,a%b);
16 }
17 
18 void divide(){
19     for(int i=1;i*i<=p;i++){
20     if(p%i) continue;
21     if(i*i == p) fac[++num] = i;
22     else{
23         fac[++num] = i;
24         fac[++num] = p/i;
25     }
26     }
27     sort(fac+1,fac+num+1);
28 }
29 
30 void read(){
31     scanf("%d%d%d",&n,&q,&p);
32     divide();
33     for(int i=1;i<=n;i++) scanf("%d",&a[i]),a[i] = gcd(a[i],p);
34     for(int i=1;i<=n;i++){
35     int z = lower_bound(fac+1,fac+num+1,a[i])-fac;
36     dt[z]++;
37     }
38     for(int i=1;i<=num;i++){
39     int hh = fac[i]; mu[i] = 1;
40     for(int j=2;j*j<=hh;j++){
41         int cnt = 0;
42         while(hh % j == 0) cnt++,hh/=j;
43         if(cnt > 1)mu[i] = 0;
44         else if(cnt) mu[i] = 1ll*mu[i]*(mod-1)%mod;
45     }
46     if(hh != 1){mu[i] = 1ll*mu[i]*(mod-1)%mod;}
47     }
48 }
49 
50 void work(){
51     pw2[0] = 1; for(int i=1;i<=n;i++) pw2[i] = pw2[i-1]*2%mod;
52     for(int i=1;i<=num;i++){
53     int z = 0;
54     for(int j=i;j<=num;j++){
55         if(fac[j] % fac[i] == 0) z+=dt[j];
56     }
57     chs[i] = (pw2[z]-1)%mod;
58     }
59     for(int i=1;i<=num;i++){
60     for(int j=i+1;j<=num;j++){
61         if(fac[j] % fac[i]) continue;
62         int ct = lower_bound(fac+1,fac+num+1,fac[j]/fac[i])-fac;
63         chs[i] = chs[i]+1ll*mu[ct]*chs[j]%mod; chs[i] %= mod;
64     }
65     }
66     for(int i=num;i>=1;i--){
67     for(int j=1;j<i;j++){
68         if(fac[i] % fac[j] == 0) chs[i] += chs[j],chs[i] %= mod;
69     }
70     }
71     for(int i=1;i<=q;i++){
72     int x; scanf("%d",&x); x = gcd(x,p);
73     x = lower_bound(fac+1,fac+num+1,x)-fac;
74     printf("%d\n",chs[x]);
75     }
76 }
77 
78 int main(){
79     read();
80     work();
81     return 0;
82 }

 

转载于:https://www.cnblogs.com/Menhera/p/10514800.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值