版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/u013578420/article/details/82020674
题目大意:
给出一组系数A, 和模数m
定义
为模线性方程
的解数
给出一个长度为n的数组B, 对于B的所有非空子集A, 和一个属于[1,M]的m
, 求所有
的异或和。
题目思路:
先证明
考虑中国剩余定理, 先将m拆成质数幂的形式, 考虑对于某个单独质数幂的答案, 则原方程的解数就是各维度下解数的乘积。
考虑
, p是质数,
由于p是质数, 对于
种与p互质的部分可以直接用逆元消去, 考虑
剩下的部分是
原方程变为
假设
种最小的是
, 将其余的部分看成一个整体
仅考虑
的取值, 方程有解的条件是
, 由于
是
中最小的那个, 所以条件成立, 方程有解, 解数就是
即
, 然后剩下的x的值可以随便取有
种取值
所以在
下的解数是
, 其中
是所有
中拥有质数
的最小指数。
在用中国剩余定理合并一下答案, 可得
现在要求
然后使用套路1: 这种gcd求和, 先预处理 , 表示所有gcd是d的倍数情况下的和
要让gcd是d的倍数, 则gcd中的每一个元素都得是d的倍数, 预处理出数组 表示B数组中有多少个数是d的倍数, 则
接着使用套路2: 容斥系数
然而求出了这个之后, 直接将 相加很明显不是答案, 一是 中对于每个gcd贡献都算作1, 实际上应该是gcd的值, 二是 考虑的是所有gcd是d的倍数的情况, 直接相加有很多重复。 然而我们还是想直接通过 的某种求和方式来表达我们相求的式子
考虑每个 前都乘上一个系数
我们想要 就是答案, 则根据定义, 需要满足
然后我们发现这个性质和 惊人的相似, 于是我就直接使用欧拉函数。
所以最终算的就是
由于最后要求的是一个异或和, 把对每个m的贡献单独存起来, 最后求一遍异或和即可。
#include <map>
#include <cmath>
#include <vector>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>
#define ll long long
#define ull unsigned ll
#define db double
#define fi first
#define se second
#define pi pair<int, int >
#define ls (x << 1)
#define rs ((x << 1) | 1)
#define mid ((l + r) >> 1)
#define mp(x, y) make_pair((x), (y))
#define pb push_back
#define sqr(x) ((x) * (x))
#define eps 1e-8
using namespace std;
const int N = (int)1e5 + 10;
const int mo = 998244353;
int tot, phi[N], prim[N]; bool ok[N];
void getPhiAndPrime(){
phi[1] = 1;
for (int i = 2; i < N; i ++){
if (!ok[i]){ok[i] = i; prim[++ tot] = i; phi[i] = i - 1;}
for (int j = 1; j <= tot; j ++){
if (1ll * i * prim[j] >= N) break;
ok[i * prim[j]] = 1;
if (i % prim[j]) phi[i * prim[j]] = phi[i] * (prim[j] - 1);
else {phi[i * prim[j]] = phi[i] * prim[j]; break;}
}
}
}
ll pw(ll x, int k){
ll ret = 1;
while (k){
if (k & 1) ret = ret * x % mo;
x = x * x % mo;
k >>= 1;
}
return ret;
}
int n, m; ll A[N], B[N], C[N], ans[N];
int main(){
getPhiAndPrime();
int T; scanf("%d", &T);
while (T --){
memset(A, 0, sizeof(A));
memset(C, 0, sizeof(C));
memset(ans, 0, sizeof(ans));
scanf("%d %d", &n, &m);
for (int i = 1; i <= n; i ++){
scanf("%lld", B + i);
A[B[i]] ++;
}
for (int i = 1; i <= m; i ++)
for (int j = i; j < N; j += i)
C[i] += A[j];
for (int i = 1; i <= m; i ++)
for (int j = i; j <= m; j += i)
(ans[j] += phi[i] * pw(j, mo - 2) % mo * (pw(j + 1, C[i]) - 1) % mo) %= mo;
ll ret = 0;
for (int i = 1; i <= m; i ++) ret ^= ans[i];
printf("%lld\n", ret);
}
return 0;
}