[Nowcoder 2018ACM多校第十场E] Rikka with Equation

题目大意:
给出一组系数A, 和模数m
定义 f(A,m) f ( A , m ) 为模线性方程 aixi=0 (mod m) ∑ a i ∗ x i = 0   ( m o d   m ) 的解数
给出一个长度为n的数组B, 对于B的所有非空子集A, 和一个属于[1,M]的m wm=f(A,m) mod 998244353 w m = ∑ f ( A , m )   m o d   998244353 , 求所有 wm w m 的异或和。 (n,m,Bi105) ( n , m , B i ≤ 10 5 )

题目思路:
先证明 f(A,m)=gcd(A,m)m|A|1 f ( A , m ) = g c d ( A , m ) ∗ m | A | − 1
考虑中国剩余定理, 先将m拆成质数幂的形式, 考虑对于某个单独质数幂的答案, 则原方程的解数就是各维度下解数的乘积。
考虑 pt p t , p是质数, aixi=0 (mod pt) ∑ a i ∗ x i = 0   ( m o d   p t )
由于p是质数, 对于 ai a i 种与p互质的部分可以直接用逆元消去, 考虑 ai a i 剩下的部分是 pqi p q i
原方程变为 pqixi=0 (mod pt) ∑ p q i ∗ x i = 0   ( m o d   p t )
假设 pqi p q i 种最小的是 pq1 p q 1 , 将其余的部分看成一个整体 C C
pq1x1+C=0 (mod pt)
仅考虑 x1 x 1 的取值, 方程有解的条件是 C | gcd(pq1,pt) C   |   g c d ( p q 1 , p t ) , 由于 pq1 p q 1 pqi p q i 中最小的那个, 所以条件成立, 方程有解, 解数就是 gcd(pq1,pt) g c d ( p q 1 , p t ) pq1 p q 1 , 然后剩下的x的值可以随便取有 pt p t 种取值
所以在 pt p t 下的解数是 pqi(qt)|A|1 p q i ∗ ( q t ) | A | − 1 , 其中 pqi p q i 是所有 ai a i 中拥有质数 p p 的最小指数。
在用中国剩余定理合并一下答案, 可得f(A,m)=gcd(A,m)m|A|1
现在要求

mAgcd(A,m)m|A|1 ∑ m ∑ A g c d ( A , m ) ∗ m | A | − 1

然后使用套路1: 这种gcd求和, 先预处理 wd w d , 表示所有gcd是d的倍数情况下的和
要让gcd是d的倍数, 则gcd中的每一个元素都得是d的倍数, 预处理出数组 Cd C d 表示B数组中有多少个数是d的倍数, 则
wd=d|mi=1Cdmi1=d|m1m(i=0Cdmi1)=d|m1m((m+1)Cd1) w d = ∑ d | m ∑ i = 1 C d m i − 1 = ∑ d | m 1 m ( ∑ i = 0 C d m i − 1 ) = ∑ d | m 1 m ∗ ( ( m + 1 ) C d − 1 )

接着使用套路2: 容斥系数
然而求出了这个之后, 直接将 wd w d 相加很明显不是答案, 一是 wd w d 中对于每个gcd贡献都算作1, 实际上应该是gcd的值, 二是 wd w d 考虑的是所有gcd是d的倍数的情况, 直接相加有很多重复。 然而我们还是想直接通过 wd w d 的某种求和方式来表达我们相求的式子
考虑每个 wd w d 前都乘上一个系数 ad a d
我们想要 adwd ∑ a d ∗ w d 就是答案, 则根据定义, ad a d 需要满足
d|nad=n ∑ d | n a d = n

然后我们发现这个性质和 ϕ(d) ϕ ( d ) 惊人的相似, 于是我就直接使用欧拉函数。
所以最终算的就是
dd|mϕ(d)1m((m+1)Cd1) ∑ d ∑ d | m ϕ ( d ) ∗ 1 m ∗ ( ( m + 1 ) C d − 1 )

由于最后要求的是一个异或和, 把对每个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;
}
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值