看到gcd,直接求也不好求,就可以先考虑一下反演。然后发现答案就是∑f(u)⋅ϕ\sum f(u)\cdot \phi∑f(u)⋅ϕ,f(u)是gcd是u的倍数的方案数。
有个结论是∑d∣xμ(d)⋅(x/d)=ϕ(x)\sum_{d|x}\mu(d)\cdot(x/d)=\phi(x)∑d∣xμ(d)⋅(x/d)=ϕ(x),就是一个容斥,最终只有与x无公共质因子的数会被算到。
首先将所有数映射到值域的一个桶上。
可以发现当计算f(u)时,只有u的倍数位置是有用的。假如这样的位置比较多的时候,可以直接FWT,否则就使用n^2的方法统计。 复杂度有待平衡。(能过)
只分析k=4的容斥,其他类推:
考虑到FWT不好计算无重复选择且无序的方案数,我们换而使用有重复选择且有序的方案数来容斥。 最后再除掉k!即可。
首先对于原序列中四个值为u的倍数的不同位置a b c d,FWT取一个4次方再搞回去会算4!=24次。
但是,很显然对于有两个重复位置的形式多算了,要减去。
如何方便的统计呢? 不妨枚举他重复位置c,然后再枚举一个有序对(a,b)。
对于使用两个c,一个有序对(a,b)的,共有C(4,2)=6种排列满足要求。都减去就可以了。
这里的a,b是可以与c重复的,这样会发现又减多了,最后加回来。
考虑有三个相同的形式(可以发现不会有四个相同的,因为s!=0),枚举相同的,对应四种排列。这四种在第一次里被算了4次,在第二次里被算了2*C(4,2)=12次,因此加回8次即可。
#include <cstdio>
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long ll;
const int N = 1e6 + 10, mo = 998244353;
ll n,k,s;
int cnt[N],mx,a[N],p[N],is[N],phi[N];
int gcd(int a,int b) {
return b == 0 ? a : gcd(b, a % b);
}
void add(ll &x,ll y){
x=(x+y)%mo;
}
ll ksm(ll x,ll y) {
ll ret = 1; for (; y; y>>=1) {
if (y & 1) ret = ret * x % mo;
x = x * x % mo;
}
return ret;
}
void init() {
n=50000;
phi[1] = 1;
for (int i = 2; i <= n; i++) {
if (!is[i]) p[++p[0]]=i,phi[i]=i-1;
for (int j=1;j<=p[0]&&p[j]*i<=n;j++){
is[p[j]*i]=1;
if (i%p[j]==0) {
phi[i*p[j]]=phi[i]*p[j];
break;
} else phi[i*p[j]]=phi[i]*(p[j]-1);
}
}
}
const int E = 7e4;
ll d[E], r[E], M = 65536,fur[E];
ll ans,buc[E],ee[E];
void fwt(ll *a, int sig) {
for (int m = 1; m < M; m <<= 1) {
for (int i = 0; i < M; i+=(m<<1)) {
for (int j = 0; j < m; j++) {
ll T = a[i + j + m];
a[i + j + m] = (a[i + j] - T);
a[i + j] = (a[i + j] + T);
}
}
}
if (sig == -1) {
ll ny = ksm(M, mo - 2);
for (int i = 0; i < M; i++) a[i] = a[i] % mo * ny % mo;
} else
for (int i = 0; i < M; i++) a[i] = a[i] % mo;
}
const int L = 800;
int main() {
freopen("choose.in","r",stdin);
// freopen("choose.out","w",stdout);
init();
cin>>n>>k>>s;
for (int i = 1; i <= n; i++) {
int x; scanf("%d",&x);a[i]=x;
cnt[x]++; mx = max(mx, x);
}
if (k == 1) {
printf("%lld\n",cnt[s]*s%mo);
return 0;
}
for (int u = 1; u <= mx; u++) {
ll c1=0,c2=0,c3=0; //abcd / abcc / abbb
ll sa=0;
if (mx / u > L) {
memset(d,0,sizeof d);
for (int i = u; i <= mx; i+=u) d[i]=cnt[i];
fwt(d,1);
for (int i = 0; i < M; i++) {
buc[i] = d[i] * d[i] % mo;
fur[i] = buc[i] * buc[i] % mo;
}
fwt(buc, -1);
fwt(fur, -1);
// memset(ee,0,sizeof ee);
// for (int i = u; i <= mx; i+=u) {
// for (int j = u; j <= mx; j+=u) {
// add(ee[i^j], cnt[i] * cnt[j]);
// }
// }
// for (int i = 0; i < M; i++) if (ee[i] != buc[i]) {
// printf("WOCAO");
// }
} else {
for (int i = u; i <= mx; i+=u) {
for (int j = u; j <= mx; j+=u) {
add(buc[i^j], cnt[i] * cnt[j]);
}
}
}
if (k == 2) sa = buc[s]; else
if (k == 3) {
int zs=0;
for (int i=u; i <= mx; i+=u) {
add(sa, buc[s^i] * cnt[i]);
zs += cnt[i];
}
if (s%u==0) {
add(sa, -3 * zs * cnt[s] % mo);
add(sa, 2 * cnt[s] % mo);
}
} else {
if (mx / u > L) {
c1 = fur[s];
// ll zz=0;
// for (int i = u; i <= mx; i+=u) {
// for (int j = u; j <= mx; j+=u) {
// add(zz, buc[s^i^j] * cnt[i] % mo * cnt[j]);
// }
// }
// if (zz!=c1) {
// printf("WOCAO2");
// }
} else {
for (int i = u; i <= mx; i+=u) {
for (int j = u; j <= mx; j+=u) {
add(c1, buc[s^i^j] * cnt[i] % mo * cnt[j]);
}
}
}
for (int i = u; i <= mx; i+=u) {
add(c2, buc[s] * cnt[i]);
}
for (int i = u; i <= mx; i+=u) {
if ((s^i)%u==0) add(c3, cnt[s^i] * cnt[i]);
}
}
if (mx / u <= L) {
for (int i = u; i <= mx; i+=u) {
for (int j = u; j <= mx; j+=u) {
add(buc[i^j], -cnt[i] * cnt[j]);
}
}
} else memset(buc,0,sizeof buc);
if (k == 4) {
sa = c1 - c2 * 6 + c3 * 8;
sa %= mo;
}
// if (sa) printf("%d %lld\n",u,sa);
add(ans,sa*phi[u]);
}
if (k>=2)ans=ksm(2,mo-2)*ans%mo;
if (k>=3)ans=ksm(3,mo-2)*ans%mo;
if (k>=4)ans=ksm(4,mo-2)*ans%mo;
cout<<(ans+mo)%mo<<endl;
}