LuoGuP3321:[SDOI2015]序列统计

Pre

错误百出。

第一次打多项式快速幂。

Solution

可以发现用多项式优化动态规划的转移。

每加入一个数,就乘上一个多项式(其实这个多项式有一点像生成函数,指数表示的数模意义下的值,系数表示的是方案的数量)。

这样就可以用多项式快速幂优化了。

于是我就\(WA\)了一发。

设\(f(i,j)\)表示已经有\(i\)个数,并且指数为\(j\)的时候的系数值。

\(f(i,j)-\sum\limits_{m*n=j}f(i-1,m)*f(i-1,n)\)

于是貌似不可做。

我尝试着从修改\(NTT\)的运算过程的角度思考,但是失败了。

看题解发现

敲黑板重点

设\(g\)是\(m\)的原根

\(f(i,log_gj)=\sum\limits_{loh_gm+log_gn=log_gj}f(i-1,log_gm)*f(i-1,log_gn)\)

可做。

Code

#include <bits/stdc++.h>
#define xx first
#define yy second
#define ll long long
using namespace std;
const int N = 17000 + 5, mod = 1004535809;
int nn, nm, x, s, n, bit, rg;
int f[N], g[N], h[N], val[N], rev[N];
inline int solve (int);
inline int mul (int u, int v) {return 1LL * u * v % mod;}
inline int add (int u, int v) {return u + v >= mod ? u + v - mod : u + v;}
inline int mns (int u, int v) {return u - v < 0 ? u - v + mod : u - v;}
inline int qpow (int u, int v, int md) {int tot = 1, base = u % md;while (v) {if (v & 1) {tot = 1LL * tot * base % md;}base = 1LL * base * base % md;v >>= 1;}return tot;}
inline void solve2 ();
inline void ntt (int *a, bool flag) {
    for (int i = 0; i < n; ++i) {
        rev[i] = (rev[i >> 1] >> 1) | ((i & 1) << (bit - 1));
        if (i > rev[i]) swap (a[i], a[rev[i]]);
    }
    for (int l = 2; l <= n; l <<= 1) {
        int wi = qpow (flag ? qpow (3, mod - 2, mod) : 3, (mod - 1) / l, mod);
        int m = l / 2;
        for (int *k = a; k != a + n; k += l) {
            int w = 1;
            for (int i = 0; i < m; ++i) {
                int tmp = mul (k[i + m], w);
                k[i + m] = mns (k[i], tmp);
                k[i] = add (k[i], tmp);
                w = mul (w, wi);
            }
        }
    }
    int inver = qpow (n, mod - 2, mod);
    for (int i = 0; i < n && flag; ++i) {
        a[i] = mul (a[i], inver);
    }
}
inline void work (int *a, int *b) {
    ntt (a, false);
    ntt (b, false);
    for (int i = 0; i < n; ++i) a[i] = mul (a[i], b[i]);
    ntt (a, true);
    ntt (b, true);
    for (int i = nm - 1; i < (nm - 1) * 2; ++i) a[i - (nm - 1)] = add (a[i - (nm - 1)], a[i]), a[i] = 0;
}
inline void fpow (int k) {
    while (k) {
        if (k & 1) work (f, g);
        for (int i = 0; i < n; ++i) h[i] = g[i];
        work (g, h);
        k >>= 1;
    }
}
int main () {
    scanf ("%d%d%d%d", &nn, &nm, &x, &s);
    n = 1, bit = 0;
    while (n <= nm - 1) n <<= 1, ++bit;
    n <<= 1, ++bit;
    rg = solve (nm);
    solve2 ();
    for (int i = 1; i <= s; ++i) {int tmp; scanf ("%d", &tmp); if (tmp % nm == 0) continue; g[val[tmp % nm]]++;}
    f[val[1]] = 1;
    fpow (nn);
    printf ("%d\n", f[val[x]]);
    return 0;
}
inline void solve2 () {
    int tg = 1;
    for (int i = 0; i < nm - 1; ++i) {
        val[tg] = i;
        tg = 1LL * tg * rg % nm;
    }
}
int q[N], top;
inline int solve (int p) {
    top = 0;
    int x = p - 1;
    for (int i = 2; i * i <= x; ++i) {
        if (x % i == 0) {
            q[++top] = i;
            while (x % i == 0) {
                x /= i;
            }
        }
    }
    if (x > 1) q[++top] = x;
    for (int i = 2; ; ++i) {
        bool flag = true;
        for (int j = 1; j <= top; ++j) {
            if (qpow (i, (p - 1) / q[j], nm) == 1) flag = false;
        }
        if (flag) {
            return i;
        }
    }
}

Conclusion

即使看了题解也不能很快做出来。

首先注意多项式乘法,就是\(work()\)函数,一定要把\(b\)数组还原(可能就我一个人错吧);

其次注意求原根的时候

if (x > 1) q[++top] = x;

一定是\(>1\)

原文地址:https://www.cnblogs.com/ChiTongZ/p/11293877.html

时间: 2024-10-26 11:14:34

LuoGuP3321:[SDOI2015]序列统计的相关文章

BZOJ 3992: [SDOI2015]序列统计 NTT+快速幂

3992: [SDOI2015]序列统计 Time Limit: 30 Sec  Memory Limit: 128 MBSubmit: 1155  Solved: 532[Submit][Status][Discuss] Description 小C有一个集合S,里面的元素都是小于M的非负整数.他用程序编写了一个数列生成器,可以生成一个长度为N的数列,数列中的每个数都属于集合S. 小C用这个生成器生成了许多这样的数列.但是小C有一个问题需要你的帮助:给定整数x,求所有可以生成出的,且满足数列中

BZOJ 3992: [SDOI2015]序列统计 [快速数论变换 生成函数 离散对数]

3992: [SDOI2015]序列统计 Time Limit: 30 Sec  Memory Limit: 128 MBSubmit: 1017  Solved: 466[Submit][Status][Discuss] Description 小C有一个集合S,里面的元素都是小于M的非负整数.他用程序编写了一个数列生成器,可以生成一个长度为N的数列,数列中的每个数都属于集合S. 小C用这个生成器生成了许多这样的数列.但是小C有一个问题需要你的帮助:给定整数x,求所有可以生成出的,且满足数列中

【BZOJ3992】[SDOI2015]序列统计 NTT+多项式快速幂

[BZOJ3992][SDOI2015]序列统计 Description 小C有一个集合S,里面的元素都是小于M的非负整数.他用程序编写了一个数列生成器,可以生成一个长度为N的数列,数列中的每个数都属于集合S. 小C用这个生成器生成了许多这样的数列.但是小C有一个问题需要你的帮助:给定整数x,求所有可以生成出的,且满足数列中所有数的乘积mod M的值等于x的不同的数列的有多少个.小C认为,两个数列{Ai}和{Bi}不同,当且仅当至少存在一个整数i,满足Ai≠Bi.另外,小C认为这个问题的答案可能

BZOJ 3992: [SDOI2015]序列统计 快速幂+NTT(离散对数下)

3992: [SDOI2015]序列统计 Description 小C有一个集合S,里面的元素都是小于M的非负整数.他用程序编写了一个数列生成器,可以生成一个长度为N的数列,数列中的每个数都属于集合S. 小C用这个生成器生成了许多这样的数列.但是小C有一个问题需要你的帮助:给定整数x,求所有可以生成出的,且满足数列中所有数的乘积mod M的值等于x的不同的数列的有多少个.小C认为,两个数列{Ai}和{Bi}不同,当且仅当至少存在一个整数i,满足Ai≠Bi.另外,小C认为这个问题的答案可能很大,因

bzoj3992 [SDOI2015]序列统计

Description 小C有一个集合S,里面的元素都是小于M的非负整数.他用程序编写了一个数列生成器,可以生成一个长度为N的数列,数列中的每个数都属于集合S. 小C用这个生成器生成了许多这样的数列.但是小C有一个问题需要你的帮助:给定整数x,求所有可以生成出的,且满足数列中所有数的乘积mod M的值等于x的不同的数列的有多少个.小C认为,两个数列{Ai}和{Bi}不同,当且仅当至少存在一个整数i,满足Ai≠Bi.另外,小C认为这个问题 的答案可能很大,因此他只需要你帮助他求出答案mod 100

SDOI2015 序列统计

Description 小C有一个集合S,里面的元素都是小于M的非负整数.他用程序编写了一个数列生成器,可以生成一个长度为N的数列,数列中的每个数都属于集合S.小C用这个生成器生成了许多这样的数列.但是小C有一个问题需要你的帮助:给定整数x,求所有可以生成出的,且满足数列中所有数的乘积mod M的值等于x的不同的数列的有多少个.小C认为,两个数列{Ai}和{Bi}不同,当且仅当至少存在一个整数i,满足Ai≠Bi.另外,小C认为这个问题的答案可能很大,因此他只需要你帮助他求出答案mod 10045

P3321 [SDOI2015]序列统计 FFT+快速幂+原根

\(\color{#0066ff}{ 题目描述 }\) 小C有一个集合S,里面的元素都是小于M的非负整数.他用程序编写了一个数列生成器,可以生成一个长度为N的数列,数列中的每个数都属于集合S.小C用这个生成器生成了许多这样的数列.但是小C有一个问题需要你的帮助:给定整数x,求所有可以生成出的,且满足数列中所有数的乘积mod M的值等于x的不同的数列的有多少个.小C认为,两个数列{Ai}和{Bi}不同,当且仅当至少存在一个整数i,满足Ai≠Bi.另外,小C认为这个问题的答案可能很大,因此他只需要你

[bzoj3992][SDOI2015]序列统计——离散对数+NTT

题目大意: 给定一个数字不超过\(m\)的集合\(S\),用\(S\)中的数生成一个长度为\(n\)的序列,求所有序列中的元素乘积模\(m\)等于\(x\)的序列的个数. 思路: 考虑最朴素的\(DP\),设\(f_{i,j}\)为选了\(i\)个数,乘积模\(m\)余\(j\)的方案数,直接转移的时间复杂度是\(O(nm^2)\)的. 不难发现每次转移的过程是相同的,矩阵加速显然不太可行,考虑将乘法形式的转移变成加法形式的转移,这样每次转移即可用NTT优化. 这里需要用到一个叫做离散对数的东西

bzoj 3992: [SDOI2015]序列统计

膝盖++,IQ-- SD总是酱紫.....吐槽+++++++ 这个乘积的形式是可以用他的原根表示成加法的!!神奇啊!!! 然后加法就很棒棒了,我们可以用生成函数这个东西来计算一下了. 然后NTT就好了!! 还有这里有一个像快速幂的东西,而且把大于模数的东西搞小,是循环卷积的形式吗??好神奇啊 原根真的是劲啊 1 #include <cstdio> 2 #include <iostream> 3 #include <cmath> 4 #define LL long lon