[BZOJ3992] [SDOI2015] 序列统计 [NTT&原根&指标&多项式快速幂]

Link
https://www.lydsy.com/JudgeOnline/problem.php?id=3992


题意
给定一个集合 S S S S S S 中元素均为小于 M M M 的非负整数。( M M M 为质数)
问:用集合中的数能够构造出多少个不同的数列(允许多次使用同一个数),
满足数列中所有数的乘积 ≡ x ( m o d M ) \equiv x\pmod{M} x(modM) x ∈ [ 1 , M − 1 ] x\in[1,M-1] x[1,M1]
答案模 1004535809 1004535809 1004535809


设计:填了前 i i i 个元素,乘积 ≡ j ( m o d M ) \equiv j\pmod{M} j(modM) 的方案数 f ( i , j ) f(i,j) f(i,j)
a n s = f ( ∣ S ∣ , x ) ( m o d p ) ans=f(|S|,x)\pmod{p} ans=f(S,x)(modp)
转移 f ( i + 1 , j ) = ∑ k ∑ t = 1 ∣ S ∣ f ( i , k ) [ S t ≡ k − 1 j ( m o d M ) ] f(i+1,j)=\sum\limits_{k}\sum\limits_{t=1}^{|S|} f(i,k)[S_t\equiv k^{-1}j\pmod{M}] f(i+1,j)=kt=1Sf(i,k)[Stk1j(modM)]


注意到 M M M 是质数就算了, M ≥ 3 M\ge3 M3 ……干什么用的?奇素数??
那先不管具体怎么样,我们思考用用原根
f ( i , j ) f(i,j) f(i,j) 表示填前 i i i 个元素,乘积 ≡ g j ( m o d M ) \equiv g^j\pmod{M} gj(modM)
f ( i + 1 , j ) ≡ ∑ k = 0 j ∑ t = 1 ∣ S ∣ f ( i , k ) [ S t ≡ g j − k ( m o d M ) ] ( m o d p ) f(i+1,j)\equiv\sum\limits_{k=0}^{j}\sum\limits_{t=1}^{|S|} f(i,k)[S_t\equiv g^{j-k}\pmod{M}]\pmod{p} f(i+1,j)k=0jt=1Sf(i,k)[Stgjk(modM)](modp)


G ( x ) = ∑ t = 1 ∣ S ∣ [ S t ≡ g x ( m o d M ) ] ( m o d p ) G(x)=\sum\limits_{t=1}^{|S|}[S_t\equiv g^{x}\pmod{M}]\pmod{p} G(x)=t=1S[Stgx(modM)](modp)
(实际上 G ( x ) ∈ { 0 , 1 } G(x) \in \{0,1\} G(x){0,1}
但是这样的话 g 0 ≡ g M − 1 ( m o d M ) g^0\equiv g^{M-1}\pmod{M} g0gM1(modM)
所以我们应该设 G ( x ) = ∑ i = 1 ∣ S ∣ [ I g ( S t ) = x ( m o d ϕ ( M ) ) ] ( m o d p ) G(x)=\sum\limits_{i=1}^{|S|}[I_g(S_t)=x\pmod{\phi(M)}]\pmod{p} G(x)=i=1S[Ig(St)=x(modϕ(M))](modp)


f ( i + 1 , j ) = ∑ k = 0 j f ( i , k ) G ( j − k ) ( m o d p ) f(i+1,j)=\sum\limits_{k=0}^{j}f(i,k)G(j-k)\pmod{p} f(i+1,j)=k=0jf(i,k)G(jk)(modp)
f ( i + 1 ) ≡ f ( i ) ⊗ G ( m o d p ) f(i+1)\equiv f(i)\otimes G\pmod{p} f(i+1)f(i)G(modp)
那么 f ( N ) ≡ G N ( m o d p ) f(N)\equiv G^{N}\pmod{p} f(N)GN(modp)
注意在卷 G G G 卷卷的时候要不停把下标馍馍 ( m o d ϕ ( M ) ) \pmod{\phi(M)} (modϕ(M))


#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstdlib>
#include<cmath>
#include<cstring>
#include<cctype>
#include<ctime>
using namespace std;
#define getchar() (frS==frT&&(frT=(frS=frBB)+fread(frBB,1,1<<12,stdin),frS==frT)?EOF:*frS++)
char frBB[1<<12], *frS=frBB, *frT=frBB;
inline void read(int& x)
{
	x = 0; char ch = getchar(); bool w = 0;
	while (!isdigit(ch)) w |= (ch == '-'), ch = getchar();
	while (isdigit(ch)) x = x * 10 + (ch ^ 48), ch = getchar();
	w ? (x = -x) : 0;
}
const int MAXN = 16384+16;
const long long p = 1004535809;
inline long long qpow(long long a, long long b, const long long& p)
{
	long long ret = 1;
	a %= p;
	while(b)
	{
		if (b & 1)
		{
			ret *= a;
			ret %= p;
		}
		a *= a;
		a %= p;
		b >>= 1;
	}
	return ret;
}
const long long g = 3;
int n, m, x, s, Lim = 1, Log;
long long q;
long long Wn[MAXN][2];
int Rev[MAXN];
inline long long Adjust(long long x)
{
	x = (x % p) + p;
	return (x >= p) ? (x - p) : x;
}
void NTT(long long *a, const bool& Type = 0)
{
	for (register int i = 0; i < Lim; ++i) if (Rev[i] > i) swap(a[Rev[i]], a[i]);
	register long long x;
	for (register int Len, Mid = 1, qwq; Mid < Lim; Mid <<= 1)
	{
		Len = Mid << 1;
		qwq = Lim / Len;
		for (register int Pos = 0; Pos < Lim; Pos += Len)
		{
			for (register int Sub = 0; Sub < Mid; ++Sub)
			{
				x = Wn[qwq * Sub][Type] * a[Pos + Mid + Sub] % p;
				a[Pos + Mid + Sub] = Adjust(a[Pos + Sub] - x);
				a[Pos + Sub] = (a[Pos + Sub] + x) % p;
			}
		}
	}
}
int gM;
int PMStack[8005],  Ind[8005];
inline void PMSplit(int x)
{
	for (register int i = 2; i * i <= x; ++i)
	{
		if (x % i == 0)
		{
			PMStack[++PMStack[0]] = i;
			while (x % i == 0) x /= i;
		}
	}
}
inline int GetPrimeRoot(int x, int phi)
{
	PMSplit(phi);
	register bool flag;
	for (register int i = 2; i < x; ++i)
	{
		flag = 0;
		for (register int j = 1; j <= PMStack[0]; ++j)
		{
			if (qpow(i, phi/PMStack[j], m) == 1)
			{
				flag = 1;
				break;
			}
		}
		if (!flag) return i;
	}
}
long long a[MAXN];
long long ta[MAXN], tb[MAXN], inv;
inline void PolyMul(long long *x, long long *y)
{
	for (register int i = 0; i < Lim; ++i) ta[i] = x[i] % p;
	for (register int i = 0; i < Lim; ++i) tb[i] = y[i] % p;
	NTT(ta); NTT(tb);
	for (register int i = 0; i < Lim; ++i) ta[i] = ta[i] * tb[i] % p;
	NTT(ta, 1);
	for (register int i = 0; i < Lim; ++i) ta[i] = ta[i] * inv % p;
	for (register int i = 0; i < m-1; ++i) x[i] = (ta[i] + ta[i+m-1]) % p;
	//同样要小心自乘的情况
}
long long Ans[MAXN];
void PolyPow(long long *a, int Index)
{
	Ans[0] = 1;
	while (Index)
	{
		if (Index & 1)
		{
			PolyMul(Ans, a);
		}
		PolyMul(a, a);
		Index >>= 1;
	}
}
int main()
{
	read(n), read(m), read(x), read(s);
	gM = GetPrimeRoot(m, m-1);
	for (register int i = 0, cur = 1; i < m - 1; ++i) Ind[cur] = i, cur *= gM, cur %= m;
	for (register int t, i = 1; i <= s; ++i) read(t), t ? (a[Ind[t]] = 1) : 0;
	int mm = m << 1;
	while (Lim <= mm) Lim <<= 1, ++Log;
	for (register int i = 0; i < Lim; ++i) Rev[i] = (Rev[i>>1]>>1)|((i&1)<<(Log-1));
	inv = qpow(Lim, p-2, p);
	q = (p - 1) / Lim;
	register long long fafa = qpow(g, q, p);
	Wn[0][0] = Wn[Lim][0] = Wn[0][1] = Wn[Lim][1] = 1;
	for (register int i = 1; i < Lim; ++i) Wn[i][0] = Wn[i-1][0] * fafa % p;
	for (register int i = 1; i < Lim; ++i) Wn[i][1] = Wn[Lim-i][0];
	PolyPow(a, n);
	printf("%lld", Ans[Ind[x]]);
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值