牛客挑战赛42 D

小睿睿的柿子

https://ac.nowcoder.com/acm/contest/6944/D

分析

类似 这样的式子,其实我们普通的快速幂是没法解决的,所以考虑拓展欧拉定理。 而题面上已经保证 了,所以可以直接根据 的来化简了 ,我们现在已经把幂降下来了。现在原问题等同于求这个东西 。那么我们现在有个初步的想法,令 表示 ,因为这个是在 意义下进行的,所以考虑枚举 有多少个数。那么总的方案数就可以写作,那么 ,这个显然是个多项式的卷积形式。所以考虑用快速数论变化优化一下,时间复杂度为 。也可以用生成函数来理解。 表示值为 可以出现的次数。那么令 的普通生成函数 ,同理可以求得 。那么最后 。有什么细节和代码上的不懂,欢迎私聊啊。

代码

#include<bits/stdc++.h>
using namespace std;
const int N = 3e6 + 10,g = 3,p = 998244353,gi = 332748118;
#define LL long long
LL L,R[N],A[N],B[N],C[N],limit = 1,inv;
LL phi(LL x) {
    LL ans = x;
    for(LL i = 2;i * i <= x;i++) {
        if(!(x % i)) {
            ans = (ans / i * (i - 1));
            while(!(x % i)) x /= i;
        }
    }
    if(x > 1) ans = (ans / x * (x - 1));
    return ans;
}
LL fastpow(LL a,LL b,LL Mod) {
    LL x = 1;
    for(;b;b >>= 1,a = a * a % Mod) {
        if(b&1) x = x * a % Mod;
    }
    return x;
}
void ntt(LL *a,LL type) {
    for(LL i = 0;i < limit;i++) {
        if(i < R[i]) swap(a[i],a[R[i]]);
    }
    for(LL mid = 1;mid < limit;mid <<= 1) {
        LL wn = fastpow((type == 1)?g:gi,(p-1)/(mid << 1),p);
        for(LL i = 0;i < limit;i += (mid << 1)) {
            LL w = 1;
            for(LL j = 0;j < mid;j++,w = w * wn % p) {
                LL x = a[i + j] , y = w * a[i + j + mid] % p;
                a[i + j] = (x + y) % p;a[i + j + mid] = (x - y + p) % p; 
            }
        }
    }
} 
LL x,y,z,n,k;
int main() {
    cin >> n >> k;
    x = n;
    y = fastpow(n,n,k - 1) + k - 1;
    z = fastpow(n,fastpow(n,n,phi(k - 1)) + phi(k - 1),k - 1) + k - 1;
//    cout << x << " " << y << " " << z << endl;
    for(int i = 1;i <= n;i++) {
        A[fastpow(i,x,k)]++;B[fastpow(i,y,k)]++;C[fastpow(i,z,k)]++;
//        cout << fastpow(i,x,k)<<" "<<fastpow(i,y,k)<<" "<<fastpow(i,z,k)<<endl;
    }
    while(limit <= k + k - 2) limit <<= 1,L++;
    inv = fastpow(limit,p - 2,p);
    for(int i = 0;i < limit;i++) R[i] = (R[i>>1]>>1)|((i&1) << (L-1));
    ntt(A,1);ntt(B,1);
    for(int i = 0;i < limit;i++) A[i] = (A[i] * B[i]) % p;

    ntt(A,-1);//for(int i = 0;i < limit;i++) cout << A[i] << endl;
    LL ans = 0;
    for(int i = 0;i < limit;i++) {
        ans = (ans + C[i % k] * inv % p * A[i] % p) % p;//cout << A[i % k] * inv % p << " " << C[i] << endl;
    }
    printf("%lld\n",ans);
    return 0;
}
全部评论

相关推荐

听说改名字就能收到offer哈:Radis写错了兄弟
点赞 评论 收藏
分享
11-03 14:38
重庆大学 Java
AAA求offer教程:我手都抬起来了又揣裤兜了
点赞 评论 收藏
分享
5 2 评论
分享
牛客网
牛客企业服务