P3321 [SDOI2015]序列统计(离散对数下NTT,乘法换加法)

Posted 繁凡さん

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了P3321 [SDOI2015]序列统计(离散对数下NTT,乘法换加法)相关的知识,希望对你有一定的参考价值。

整理的算法模板合集: ACM模板

点我看算法全家桶系列!!!

实际上是一个全新的精炼模板整合计划


Weblink

https://www.luogu.com.cn/problem/P3321

Problem
在这里插入图片描述
Solution

关于离散对数的概念及其应用建议看我的《算法竞赛中的初等数论》 中的 0x61.3 整数的指标(也称指数、离散对数)哦还没更新到这儿啊,那没事了

题解建议看这里:https://www.luogu.com.cn/blog/ZigZagKmp/solution-p3321(借用离散对数将乘法转换成加法之后就是一个经典的计数问题了,非常简单。他写的实在是太好了,然后因为后天就要考毛概了我还没开始复习,题解我就懒得写了…

不过他是先倍增预处理然后再将 n n n 二进制拆分,再组成 n n n 的方式,其实直接快速幂即可,一样是 O ( l o g n ) O(logn) O(logn) 常数会更小一点,还好写一些。

Code

// Problem: P3321 [SDOI2015]序列统计
// Contest: Luogu
// URL: https://www.luogu.com.cn/problem/P3321
// Memory Limit: 125 MB
// Time Limit: 1000 ms
//
// Powered by CP Editor (https://cpeditor.org)

#include <bits/stdc++.h>

using namespace std;
#define int long long
using ll = long long;
const int N = 2e6 + 7, mod = 1004535809, G = 3;

ll a[N], b[N], h[N];
int n, m, X, S, GG;
int limit, RR[N], L;
ll g[N], f[N], dtol[N], ltod[N];
vector<int>factor;

int qpow(int a, int b, int mod = 1004535809)
{
    int res = 1;
    while(b){
        if(b & 1) res = 1ll * res * a % mod;
        a = 1ll * a * a % mod;
        b >>= 1;
    }
    return res;
}

vector<int> get_factor(ll n)
{
    vector<int>res;
    for(int i = 1; i * i <= n; ++ i) {
        if(n % i == 0) {
            res.push_back(i);
            if(i != n / i) {
                res.push_back(n / i);
            }
        }
    }
    return res;
} 

void get_RT(int n) 
{
    ll phi_m = n - 1;
    factor = get_factor(phi_m);
    for(int i = 2; i < n; ++ i) {
        bool flag = 1;
        for(int j = 0; j < (int)factor.size(); ++ j) {
            if(factor[j] != phi_m && qpow(i, factor[j], n) == 1) {
                flag = 0;
                break;
            }
        }
        if(flag) {
            GG = i;
            break;
        }
    }
    int tmp = 1;
    for(int i = 0; i < phi_m; ++ i, tmp = 1ll * tmp * GG % n)
    	dtol[tmp] = i, ltod[i] = tmp;
    return ;
}

ll inv(ll x) {return qpow(x, mod - 2);}

void NTT(ll *A, int type = 1)
{
    for(int i = 0; i < limit; ++ i)
        if(i < RR[i])
            swap(A[i], A[RR[i]]);
    for(int mid = 1; mid < limit; mid <<= 1) {
        ll wn = qpow(G, (mod - 1) / (mid * 2));
        if(type == -1) wn = qpow(wn, mod - 2);
        for(int len = mid << 1, pos = 0; pos < limit; pos += len) {
            ll w = 1;
            for(int k = 0; k < mid; ++ k, w = (w * wn) % mod) {
                int x = A[pos + k], y = w * A[pos + mid + k] % mod;
                A[pos + k] = (x + y) % mod;
                A[pos + k + mid] = (x - y + mod) % mod;

            }
        }
    }

    if(type == -1) {
        ll limit_inv = inv(limit);
        for(int i = 0; i < limit; ++ i)
            A[i] = (A[i] * limit_inv) % mod;
    }
}

void mul(ll *f, ll *g, ll *ans, int n, int m, int mod_len)
{
	limit = 1, L = 0;
	while(limit < n + m - 1) limit <<= 1, ++ L;
	for(int i = 0; i < limit; ++ i)
		RR[i] = (RR[i >> 1] >> 1) | ((i & 1) << (L - 1));	
		 
	for(int i = 0; i < n; ++ i)
		a[i] = f[i];
	for(int i = n; i < limit; ++ i)
		a[i] = 0; 
	for(int i = 0; i < m; ++ i)
		b[i] = g[i];
	for(int i = m; i < limit; ++ i)
		b[i] = 0;

	NTT(a), NTT(b);

	for(int i = 0; i < limit; ++ i) {
		a[i] = 1ll * a[i] * b[i] % mod;
	}
	NTT(a, -1);

	for(int i = 0; i < mod_len; ++ i)
		ans[i] = a[i];
	for(int i = mod_len; i < limit; ++ i)
		ans[i % mod_len] = (ans[i % mod_len] + a[i]) % mod;
	for(int i = mod_len; i < limit; ++ i)
		ans[i] = 0;
}
 
signed main()
{
	scanf("%lld%lld%lld%lld", &n, &m, &X, &S);
	get_RT(m); 
	for(int i = 1; i <= S; ++ i) {
		int x;
		scanf("%lld", &x);
		if(x != 0) {
			f[dtol[x]] ++ ;
		}
	}  
	g[0] = 1;	
	while(n) {
		if(n & 1) mul(f, g, g, m - 1, m - 1, m - 1);
		mul(f, f, f, m - 1, m - 1, m - 1);
		n >>= 1;
	}

	printf("%lld\\n", g[dtol[X]]);
	return 0;
}

以上是关于P3321 [SDOI2015]序列统计(离散对数下NTT,乘法换加法)的主要内容,如果未能解决你的问题,请参考以下文章

P3321 [SDOI2015]序列统计(未解决)

P3321 [SDOI2015]序列统计(未解决)

P3321 [SDOI2015]序列统计(未解决)

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

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

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