UOJ #450 复读机

题目链接

题目大意

有排成一行 n n n个物品和 k k k个人,现在要给每个人分配一些物品,要求每个人分到的物品都是 d d d的倍数。求分配的方案数。对 19491001 19491001 19491001取模。

数据范围:

n ≤ 1 0 9 , d ≤ 3 n\le 10^9,d\le 3 n109,d3

  • d = 2 , k ≤ 5 ∗ 1 0 5 d=2,k\le 5*10^5 d=2,k5105
  • d = 3 , k ≤ 1000 d=3,k\le 1000 d=3,k1000
思路

考虑分 d d d的三种情况讨论:

  • d = 1 d=1 d=1

答案显然,每个人拿到每一个物品都是合法的。 k n k^n kn

  • d = 2 d=2 d=2

定义序列 a [ i ] a[i] a[i]表示第 i i i个人拿到的物品个数。因为我们是在解决一个排列问题,考虑这个序列的指数型生成函数,因为必须拿偶数个,故生成函数的只有偶次项存在。

g ( x ) = ( ∑ i = 0 ∞ x 2 i ( 2 i ) ! ) k g(x)=(\sum_{i=0}^\infty \frac {x^{2i}}{(2i)!})^k g(x)=(i=0(2i)!x2i)k

我们要求的是他的 n n n次项系数,故最后的答案为 n ! ∗ g ( x ) n!*g(x) n!g(x)

考虑 e x e^x ex e − x e^{-x} ex的泰勒展开,化简 g ( x ) = ( e x + e − x 2 ) k g(x)=(\frac {e^x+e^{-x}}{2})^k g(x)=(2ex+ex)k

1 2 \frac 1 2 21提出来,答案化为 n ! ∗ 1 2 k ( e x + e − x ) k n!*\frac 1 {2^k}(e^x+e^{-x})^k n!2k1(ex+ex)k

二项式定理展开后面那部分得 ∑ i = 0 k ( k i ) e i x ∗ e ( i − k ) x = ∑ i = 0 k ( k i ) e ( 2 i − k ) x \sum\limits_{i=0}^k {k\choose i}e^{ix}*e^{(i-k)x}=\sum\limits_{i=0}^k {k\choose i}e^{(2i-k)x} i=0k(ik)eixe(ik)x=i=0k(ik)e(2ik)x

再把他代回泰勒展开的形式,则答案为 n ! ∗ 1 2 k ∑ i = 0 k ( k i ) ∑ j = 0 ∞ ( 2 i − k ) j x j j ! n!*\frac 1 {2^k} \sum\limits_{i=0}^k {k\choose i}\sum\limits _{j=0}^\infty (2i-k)^j\frac {x^j}{j!} n!2k1i=0k(ik)j=0(2ik)jj!xj

由于我们只关心他 n n n次项系数,故第二个求和符号可以去掉得 n ! ∗ 1 2 k ∑ i = 0 k ( k i ) ( 2 i − k ) n x n n ! n!*\frac 1 {2^k}\sum_{i=0}^k {k\choose i}(2i-k)^n \frac {x^n}{n!} n!2k1i=0k(ik)(2ik)nn!xn。所以最后答案为 1 2 k ∑ i = 0 k ( k i ) ( 2 i − k ) n \frac 1 {2^k}\sum\limits_{i=0}^k{k\choose i}(2i-k)^n 2k1i=0k(ik)(2ik)n

  • d = 3 d=3 d=3

仿照 d = 2 d=2 d=2,可以列出生成函数: ∑ i = 0 ∞ x 3 i ( 3 i ) ! \sum\limits_{i=0}^\infty \frac {x^{3i}}{(3i)!} i=0(3i)!x3i

但是注意到这里并没有偶次项那种优美的性质,无法泰勒展开简单求解。现在要引入一个东西:单位根。

对于一个有原根的数 p p p,若 p − 1 ∣ n p-1|n p1n,则称 p p p存在n个 n n n次单位根,第 i i i个记做 ω n i \omega_n^i ωni
单位根有一个很好的性质: ∑ i = 0 n − 1 ω n k i = 1 n [ n ∣ k ] \sum_{i=0}^{n-1} \omega_n^{ki}=\frac 1 n [n|k] i=0n1ωnki=n1[nk],被称作单位根反演。证明貌似是要通过ki遍历所有剩余系意会一下。

那么运用反演,将式子变化得到: ∑ i = 0 ∞ x i ∗ [ 3 ∣ i ] i ! ∗ 1 3 \sum\limits_{i=0}^\infty \frac {x^i*[3|i]}{i!}*\frac 1 3 i=0i!xi[3i]31。注意这里的 i i i已经和上面的 i i i不是一个意义了,一个是枚举倍数一个是枚举数。

因为模数 19491001 − 1 19491001-1 194910011是3的倍数,故存在三次单位根,设为 r r r。继续推式子得 1 3 ∑ i = 0 ∞ x i ( r 0 + r i + r 2 i ) i ! \frac 1 3\sum\limits_{i=0}^\infty \frac {x^i(r^0+r^i+r^{2i})}{i!} 31i=0i!xi(r0+ri+r2i)

泰勒展开,得 e x + e r x + e r 2 x 3 \frac {e^x+e^{rx}+e^{r^2x}}{3} 3ex+erx+er2x,整个序列就对应着 ( e x + e r x + e r 2 x 3 ) k (\frac {e^x+e^{rx}+e^{r^2x}}{3})^k (3ex+erx+er2x)k,我们要求他的 n n n次项系数并乘 n ! n! n!

老规矩,提出来 3 k 3^k 3k,但是发现我们并没有什么好办法去展开三项式(也没有什么三项式定理)。那怎么办呢?大力展开两次二项式定理: n ! 1 3 k ∑ i = 0 k ( k i ) e i x ∑ j = 0 k − j ( k − j j ) e r j x e r 2 ( k − i − j ) x = n ! 1 3 k ∑ i = 0 k ( k i ) ∑ j = 0 k − i ( k − i j ) e [ i + r j + r 2 ( k − i − j ) ] x n!\frac 1 {3^k}\sum\limits_{i=0}^k{k\choose i}e^{ix}\sum\limits_{j=0}^{k-j}{k-j\choose j}e^{rjx}e^{r^2(k-i-j)x}=n!\frac 1 {3^k}\sum\limits_{i=0}^k{k\choose i}\sum\limits_{j=0}^{k-i}{k-i\choose j}e^{[i+rj+r^2(k-i-j)]x} n!3k1i=0k(ik)eixj=0kj(jkj)erjxer2(kij)x=n!3k1i=0k(ik)j=0ki(jki)e[i+rj+r2(kij)]x

i x + r j x + r 2 ( k − i − j ) = A ix+rjx+r^2(k-i-j)=A ix+rjx+r2(kij)=A,打起来太烦了。老规矩,泰勒展开代回去得: n ! 1 3 k ∑ i = 0 k ∑ j = 0 k − i ( k i ) ( k − i j ) ∑ p = 0 ∞ A p x p p ! n!\frac 1 {3^k}\sum\limits_{i=0}^k\sum\limits_{j=0}^{k-i}{k\choose i}{k-i\choose j}\sum\limits_{p=0}^\infty A^p\frac {x^p}{p!} n!3k1i=0kj=0ki(ik)(jki)p=0App!xp

依然我们只需要求 n n n次项系数,消去最后一个求和符号得 n ! 1 3 k ∑ i = 0 k ∑ j = 0 k − i ( k i ) ( k − i j ) A n x n n ! n!\frac 1 {3^k}\sum\limits_{i=0}^k\sum\limits_{j=0}^{k-i}{k\choose i}{k-i\choose j}A^n\frac {x^n}{n!} n!3k1i=0kj=0ki(ik)(jki)Ann!xn

所以最后只需要求 1 3 k ∑ i = 0 k ∑ j = 0 k − i ( k i ) ( k − i j ) A n \frac 1 {3^k}\sum\limits_{i=0}^k\sum\limits_{j=0}^{k-i}{k\choose i}{k-i\choose j}A^n 3k1i=0kj=0ki(ik)(jki)An即可。不得不说出题人的分段数据范围还是很坑。

场上为了得分也是很用功了把所有SubTask全分开写了。。

代码
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define pa pair
#define INF 0x3f3f3f3f
#define inf 0x3f
#define fi first
#define se second
#define mp make_pair
#define ll long long
#define ull unsigned long long
#define pb push_back

using namespace std;

inline ll read()
{
	long long f=1,sum=0;
	char c=getchar();
	while (!isdigit(c)) {if (c=='-') f=-1;c=getchar();}
	while (isdigit(c)) {sum=sum*10+c-'0';c=getchar();}
	return sum*f;
}
#define Mod 19491001
inline ll quickpow(ll a,int b)
{
	ll ret=1;
	a%=Mod;
	while (b)
	{
		if (b&1) ret*=a,ret%=Mod;
		a*=a,a%=Mod,b/=2;
	}
	return ret;
}
namespace SubTask2
{
	const int MAXN=1010;
	const int MAXK=110;
	ll fac[MAXN],inv[MAXN],f[MAXK][MAXN];
	inline ll C(int n,int m) {return fac[n]*inv[m]%Mod*inv[n-m]%Mod;}
	void solve(int n,int k,int d)
	{
		fac[0]=1;
		for (int i=1;i<=n;i++) fac[i]=fac[i-1]*i%Mod;
		inv[n]=quickpow(fac[n],Mod-2);
		for (int i=n-1;i>=0;i--) inv[i]=inv[i+1]*(i+1)%Mod;
		f[0][0]=1;
		for (int i=1;i<=k;i++)
			for (int j=0;j<=n;j++)
				for (int p=0;p<=j;p+=d)
					f[i][j]+=f[i-1][j-p]*C(j,p),f[i][j]%=Mod;
		cout<<f[k][n]<<'\n';
	}
}
namespace SubTask3
{
	const int MAXK=500010;
	ll fac[MAXK],inv[MAXK];
	inline ll C(int n,int m) {return fac[n]*inv[m]%Mod*inv[n-m]%Mod;}
	void solve(int n,int k,int d)
	{
		fac[0]=1;
		for (int i=1;i<=k;i++) fac[i]=fac[i-1]*i%Mod;
		inv[k]=quickpow(fac[k],Mod-2);
		for (int i=k-1;i>=0;i--) inv[i]=inv[i+1]*(i+1)%Mod;
		ll ans=0;
		for (int i=0;i<=k;i++)
			ans+=C(k,i)*quickpow((2*i-k+Mod)%Mod,n)%Mod,ans%=Mod;
		ans*=quickpow(quickpow(2,k),Mod-2);
		ans%=Mod;
		cout<<ans;
	}
}
namespace SubTask4
{
	#define g 7
	const int MAXK=1010;
	ll fac[MAXK],inv[MAXK];
	inline ll C(int n,int m) {return fac[n]*inv[m]%Mod*inv[n-m]%Mod;}
	void solve(int n,int k,int d)
	{
		fac[0]=1;
		for (int i=1;i<=k;i++) fac[i]=fac[i-1]*i%Mod;
		inv[k]=quickpow(fac[k],Mod-2);
		for (int i=k-1;i>=0;i--) inv[i]=inv[i+1]*(i+1)%Mod;
		ll ans=0,r=quickpow(g,(Mod-1)/3),r_2=r*r%Mod;		
		for (int i=0;i<=k;i++)
			for (int j=0;j<=k-i;j++)
				ans+=C(k,i)*C(k-i,j)%Mod*quickpow((i+r*j+r_2*(k-i-j))%Mod,n),ans%=Mod;
		ans*=quickpow(quickpow(3,k),Mod-2);
		ans%=Mod;
		cout<<ans;
	}
}
int main()
{
	int n=read(),k=read(),d=read();
	if (d==1) cout<<quickpow(k,n)<<'\n';
	else if (n%d) cout<<0<<'\n';
	else if (n<=1000 && k<=100) SubTask2::solve(n,k,d);
	else if (d==2) SubTask3::solve(n,k,d);
	else if (d==3) SubTask4::solve(n,k,d);
	return 0;
}

你可能感兴趣的:(数学)