洛谷 - 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]=ab≡j(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,...,gm−1 各不相同,且值域属于 [ 1 , m ) [1,m) [1,m)
所以将每个数换成其原根对应的数字后就可以化乘为加了,设 g A ≡ a ( m o d m ) g^A \\equiv a \\pmod m gA≡a(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]=gAgB≡gJ(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(modm−1)≡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+B≡J(modm−1)∑f[i][A]∗f[i][B]
又因为每层的卷积的转移方程是相同的,可以用快速幂优化NTT,时间复杂度为 O ( m ∗ l o g m ∗ l o g n ) O(m*logm*logn) O(m∗logm∗logn)
有几个小细节需要注意:
- 因为模数从 m m m 变成了 m − 1 m-1 m−1,原本 g m − 1 ≡ 1 ( m o d m ) g^{m-1}\\equiv 1 \\pmod m gm−1≡1(modm),这里的指数需要对 m − 1 m-1 m−1 取模,换算到本题中应该变成 g m − 1 = g 0 = 1 g^{m-1}=g^{0}=1 gm−1=g0=1,所以当原数为 1 1 1 时,对应的原根的指数应该为 0 0 0,几乎没有题解说明这个细节,自己想了好久才明白的
- S S S 集合中的 0 0 0 是没有贡献的,直接忽略即可
- 每次卷完之后,因为下标的取值是 [ 0 , 2 ∗ ( m − 1 ) ) [0,2*(m-1)) [0,2∗(m−1)) 的,所以需要将 [ m − 1 , 2 ∗ ( m − 1 ) ) [m-1,2*(m-1)) [m−1,2∗(m−1)) 这一部分的答案加到 [ 0 , m − 1 ) [0,m-1) [0,m−1) 上去
代码:
// 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]序列统计(未解决)
luogu P3321 [SDOI2015]序列统计 FFT