题意:
一个序列
a
1
,
.
.
.
,
a
n
a_1,...,a_n
a1,...,an 是合法的,当且仅当:
长度为给定的
n
n
n。
a
1
,
.
.
.
,
a
n
a_1,...,a_n
a1,...,an 都是[1,A]中的整数。
a
1
,
.
.
.
,
a
n
a_1,...,a_n
a1,...,an 互不相等。
一个序列的值定义为它里面所有数的乘积,即
a
1
a
2
.
.
.
a
n
a_1a_2...a_n
a1a2...an。
求所有不同合法序列的值的和。
两个序列不同当且仅当他们任意一位不一样。
输出答案对一个数
m
o
d
mod
mod取余的结果。
数据范围:
0
:
A
≤
10
,
n
≤
10.
0:A\leq10,n\leq10.
0:A≤10,n≤10.
1..3
:
A
≤
1000
,
n
≤
20.
1..3:A\leq1000,n\leq20.
1..3:A≤1000,n≤20.
4..9
:
A
≤
1
0
9
,
n
≤
20.
4..9:A\leq10^9,n\leq20.
4..9:A≤109,n≤20.
10..19
:
A
≤
1
0
9
,
n
≤
500.
10..19:A\leq10^9,n\leq500.
10..19:A≤109,n≤500.
m
o
d
≤
1
0
9
,
m
o
d
mod\leq10^9,mod
mod≤109,mod 为素数,
m
o
d
>
A
>
n
+
1
mod>A>n+1
mod>A>n+1
Analysis:
首先解决互不相同,不妨将最后序列排序,从小到大统计方案再乘上
n
!
n!
n!即可。
于是我们可以设
f
i
,
j
f_{i,j}
fi,j,表示当前考虑到
[
1..
i
]
[1..i]
[1..i],选了
j
j
j个数的方案。
可以
O
(
n
A
)
O(nA)
O(nA)转移,每次考虑当前
i
i
i选不选。能通过前
4
4
4档部分分。
考虑优化转移,套路地去想能不能从
f
A
,
i
f_{A,i}
fA,i推到
f
2
∗
A
,
i
f_{2*A,i}
f2∗A,i。
我们考虑如此一个式子:
∏
(
a
i
+
A
)
=
∑
S
⊂
a
0
.
.
a
n
A
n
−
∣
S
∣
∗
∏
a
i
∈
S
a
i
\prod(a_i+A)=\sum_{S\subset {a_0..a_n}}A^{n-|S|}*\prod_{a_i\in{S}}a_i
∏(ai+A)=S⊂a0..an∑An−∣S∣∗ai∈S∏ai
通过这个式子我们也许就有了从
A
A
A推到
2
A
2A
2A的方法了。
我们考虑设在
[
1
,
A
]
[1,A]
[1,A]里选
i
i
i个数的价值和为
a
i
a_i
ai
在
[
A
+
1..2
A
]
[A+1..2A]
[A+1..2A]选
i
i
i个数的价值和为
b
i
b_i
bi
那么有:
f
2
A
,
i
=
∑
j
=
0
i
a
j
∗
b
i
−
j
f_{2A,i}=\sum_{j=0}^ia_j*b_{i-j}
f2A,i=∑j=0iaj∗bi−j
怎么算
b
i
b_i
bi?用刚刚那个式子
b
i
=
∑
j
=
0
i
A
i
−
j
∗
a
i
∗
(
i
−
j
A
−
i
)
b_i=\sum_{j=0}^iA^{i-j}*a_i*(^{A-i}_{i-j})
bi=j=0∑iAi−j∗ai∗(i−jA−i)
表示枚举那个子集,那么后面的乘积部分可以用
a
i
a_i
ai表示出来,前面的系数是
a
i
a_i
ai乘上有多少个这样的子集,也就是那个组合数。
组合数我们一般都可以用下降幂的形式表示出来。
设
m
i
=
∏
j
=
0
i
−
1
(
A
−
j
)
,
n
m
i
=
m
i
−
1
m_i=\prod_{j=0}^{i-1}(A-j),nm_i=m_i^{-1}
mi=∏j=0i−1(A−j),nmi=mi−1。那么它就是:
(
i
−
j
)
!
−
1
∗
m
i
∗
n
m
j
(i-j)!^{-1}*m_i*nm_{j}
(i−j)!−1∗mi∗nmj。
现在
b
i
b_i
bi就可以算出来了。
我们考虑当
A
A
A为奇数时,递归
A
−
1
A-1
A−1,然后
O
(
n
)
O(n)
O(n)递推到
A
A
A。
为偶数直接递归
A
/
2
A/2
A/2,
O
(
n
2
)
O(n^2)
O(n2)变换到
A
A
A。
复杂度
O
(
n
2
log
A
)
O(n^2\log{A})
O(n2logA)。
Code:
# include<cstdio>
# include<cstring>
# include<algorithm>
using namespace std;
const int N = 500 + 5;
typedef long long ll;
int f[65][N],inv[N],fac_[N],c[N];
int mi[N],nmi[N],b[N],a[N];
int n,A,mo;
inline int pow(int x,int p)
{
int ret = 1;
for (; p ; p >>= 1,x = (ll)x * x % mo)
if (p & 1) ret = (ll)ret * x % mo;
return ret;
}
inline void calc(int B,int d)
{
if (B == 1) { f[d][0] = f[d][1] = 1; return; }
if (B & 1)
{
calc(B - 1,d + 1); int A = B; f[d][0] = 1;
for (int i = 1 ; i <= n ; ++i) f[d][i] = (f[d + 1][i] + (ll)f[d + 1][i - 1] * A % mo) % mo;
}else
{
calc(B >> 1,d + 1); int A = B >> 1;
for (int i = 0 ; i <= n ; ++i) a[i] = f[d + 1][i];
mi[0] = nmi[0] = c[0] = 1;
for (int i = 0 ; i < n ; ++i) mi[i + 1] = (ll)mi[i] * (A - i) % mo;
for (int i = 1 ; i <= n ; ++i) c[i] = (ll)c[i - 1] * A % mo,nmi[i] = pow(mi[i],mo - 2);
for (int i = 0 ; i <= n ; ++i)
{
b[i] = 0;
for (int j = 0 ; j <= i ; ++j) b[i] = (b[i] + (ll)c[i - j] * a[j] % mo * nmi[j] % mo * fac_[i - j] % mo) % mo;
b[i] = (ll)b[i] * mi[i] % mo;
}
for (int i = 0 ; i <= n ; ++i)
for (int j = 0 ; j <= i ; ++j) f[d][i] = (f[d][i] + (ll)a[j] * b[i - j] % mo) % mo;
}
}
int main()
{
scanf("%d%d%d",&A,&n,&mo),inv[1] = fac_[0] = fac_[1] = 1;
for (int i = 2 ; i <= n ; ++i)
inv[i] = (ll)(mo - mo / i) * inv[mo % i] % mo,fac_[i] = (ll)fac_[i - 1] * inv[i] % mo;
calc(A,0); printf("%d\n",(ll)f[0][n] * pow(fac_[n],mo - 2) % mo);
return 0;
}