洛谷 - P3321 [SDOI2015]序列统计(原根+NTT)

Posted Frozen_Guardian

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了洛谷 - P3321 [SDOI2015]序列统计(原根+NTT)相关的知识,希望对你有一定的参考价值。

题目链接:点击查看

题目大意:给出一个集合 S S S,集合中的数是 [ 0 , m ) [0,m) [0,m) 且互不相同的,问从集合中选 n n n 次数字,且乘积对 m m m 取模后等于 x x x 的方案数有多少

题目分析:

考虑转移方程,我们设 f [ i ] [ j ] f[i][j] f[i][j] 为选了 i i i 个数后,乘积对 m m m 取余后为 j j j 的方案数,那么自然有 f [ i + 1 ] [ j ] = ∑ a b ≡ j ( m o d m ) f [ i ] [ a ] ∗ f [ i ] [ b ] f[i+1][j]=\\sum\\limits_{ab\\equiv j \\pmod m}f[i][a]*f[i][b] f[i+1][j]=abj(modm)f[i][a]f[i][b]

需要注意到本题的 m m m 是一个特别小的质数,不难想到原根,设 g g g m m m 的原根,可以得到一个性质就是 g 1 , g 2 , . . . , g m − 1 g^1,g^2,...,g^{m-1} g1,g2,...,gm1 各不相同,且值域属于 [ 1 , m ) [1,m) [1,m)

所以将每个数换成其原根对应的数字后就可以化乘为加了,设 g A ≡ a ( m o d m ) g^A \\equiv a \\pmod m gAa(modm) 更具体的,原转移方程就可以书写为: f [ i + 1 ] [ j ] = ∑ g A g B ≡ g J ( m o d m ) f [ i ] [ A ] ∗ f [ i ] [ B ] f[i+1][j]=\\sum\\limits_{g^Ag^B\\equiv g^J \\pmod m}f[i][A]*f[i][B] f[i+1][j]=gAgBgJ(modm)f[i][A]f[i][B] 因为 m m m 是质数,所以根据费马小定理降幂得到: f [ i + 1 ] [ j ] = ∑ g A + B ( m o d m − 1 ) ≡ g J ( m o d m ) f [ i ] [ A ] ∗ f [ i ] [ B ] f[i+1][j]=\\sum\\limits_{g^{A+B\\pmod {m-1}}\\equiv g^J \\pmod m}f[i][A]*f[i][B] f[i+1][j]=gA+B(modm1)gJ(modm)f[i][A]f[i][B]

f [ i + 1 ] [ j ] = ∑ A + B ≡ J ( m o d m − 1 ) f [ i ] [ A ] ∗ f [ i ] [ B ] f[i+1][j]=\\sum\\limits_{A+B\\equiv J \\pmod {m-1}}f[i][A]*f[i][B] f[i+1][j]=A+BJ(modm1)f[i][A]f[i][B]

又因为每层的卷积的转移方程是相同的,可以用快速幂优化NTT,时间复杂度为 O ( m ∗ l o g m ∗ l o g n ) O(m*logm*logn) O(mlogmlogn)

有几个小细节需要注意:

  1. 因为模数从 m m m 变成了 m − 1 m-1 m1,原本 g m − 1 ≡ 1 ( m o d m ) g^{m-1}\\equiv 1 \\pmod m gm11(modm),这里的指数需要对 m − 1 m-1 m1 取模,换算到本题中应该变成 g m − 1 = g 0 = 1 g^{m-1}=g^{0}=1 gm1=g0=1,所以当原数为 1 1 1 时,对应的原根的指数应该为 0 0 0,几乎没有题解说明这个细节,自己想了好久才明白的
  2. S S S 集合中的 0 0 0 是没有贡献的,直接忽略即可
  3. 每次卷完之后,因为下标的取值是 [ 0 , 2 ∗ ( m − 1 ) ) [0,2*(m-1)) [0,2(m1)) 的,所以需要将 [ m − 1 , 2 ∗ ( m − 1 ) ) [m-1,2*(m-1)) [m1,2(m1)) 这一部分的答案加到 [ 0 , m − 1 ) [0,m-1) [0,m1) 上去

代码:

// 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)

// #pragma GCC optimize(2)
// #pragma GCC optimize("Ofast","inline","-ffast-math")
// #pragma GCC target("avx,sse2,sse3,sse4,mmx")
#include<iostream>
#include<cstdio>
#include<string>
#include<ctime>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<stack>
#include<climits>
#include<queue>
#include<map>
#include<set>
#include<sstream>
#include<cassert>
#include<bitset>
#include<list>
#include<unordered_map>
#define lowbit(x) (x&-x)
using namespace std;
typedef long long LL;
typedef unsigned long long ull;
template<typename T>
inline void read(T &x)
{
	T f=1;x=0;
	char ch=getchar();
	while(0==isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
	while(0!=isdigit(ch)) x=(x<<1)+(x<<3)+ch-'0',ch=getchar();
	x*=f;
}
template<typename T>
inline void write(T x)
{
	if(x<0){x=~(x-1);putchar('-');}
    if(x>9)write(x/10);
    putchar(x%10+'0');
}
const int inf=0x3f3f3f3f;
const int N=1e6+100;
const int mod=1004535809,G=3,Gi=(mod+1)/3; 
bool vis[8010];
int n,m,limit = 1,L,r[N],p[8010];
LL a[N],ans[N];
inline LL fastpow(LL a, LL k) {
	LL base = 1;
	while(k) {
		if(k & 1) base = (base * a ) % mod;
		a = (a * a) % mod;
		k >>= 1;
	}
	return base % mod;
}
inline void NTT(LL *A, int type) {
	for(int i = 0; i < limit; i++) 
		if(i < r[i]) swap(A[i], A[r[i]]);
	for(int mid = 1; mid < limit; mid <<= 1) {	
		LL Wn = fastpow( type == 1 ? G : Gi , (mod - 1) / (mid << 1));
		for(int j = 0; j < limit; j P3321 [SDOI2015]序列统计(未解决)

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

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

luogu P3321 [SDOI2015]序列统计 FFT

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

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