题目链接
有排成一行 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 n≤109,d≤3
考虑分 d d d的三种情况讨论:
答案显然,每个人拿到每一个物品都是合法的。 k n k^n kn
定义序列 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} e−x的泰勒展开,化简 g ( x ) = ( e x + e − x 2 ) k g(x)=(\frac {e^x+e^{-x}}{2})^k g(x)=(2ex+e−x)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+e−x)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=0∑k(ik)eix∗e(i−k)x=i=0∑k(ik)e(2i−k)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=0∑k(ik)j=0∑∞(2i−k)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!∗2k1∑i=0k(ik)(2i−k)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=0∑k(ik)(2i−k)n
仿照 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 p−1∣n,则称 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=0n−1ωnki=n1[n∣k],被称作单位根反演。证明貌似是要通过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=0∑∞i!xi∗[3∣i]∗31。注意这里的 i i i已经和上面的 i i i不是一个意义了,一个是枚举倍数一个是枚举数。
因为模数 19491001 − 1 19491001-1 19491001−1是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=0∑∞i!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=0∑k(ik)eixj=0∑k−j(jk−j)erjxer2(k−i−j)x=n!3k1i=0∑k(ik)j=0∑k−i(jk−i)e[i+rj+r2(k−i−j)]x
令 i x + r j x + r 2 ( k − i − j ) = A ix+rjx+r^2(k-i-j)=A ix+rjx+r2(k−i−j)=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=0∑kj=0∑k−i(ik)(jk−i)p=0∑∞App!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=0∑kj=0∑k−i(ik)(jk−i)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=0∑kj=0∑k−i(ik)(jk−i)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;
}