题目描述
你在一个有 n n n 个城市的国家中行走,城市从 1 1 1 到 n n n 依次编号
任意两个城市之间都有一条双向道路可以通行,且你可以花一天的时间从当前所在的城市到达任意一个别的城市
由于你比较闲的无聊,所以你不会连续两天都呆在同一个城市,也就是说每天你所在的城市都不相同(这句话的意思是,对于相邻的两天,你所在的城市应该不同)
一开始你在 1 1 1 号城市,求经过 k k k 天后你回到 1 1 1 号城市的方案数
当然如果不存在任意一种方案就输出 0 0 0 就好了
输入描述
第一行两个整数 n , k n,k n,k
输出描述
一行一个整数表示答案对 998244353 998244353 998244353 取模后的结果
示例1
输入 |
---|
1 1 |
输出 |
0 |
备注:
maker.cpp如下:
#include "bits/stdc++.h"
using namespace std;
int main() {
srand((unsigned long long) new char);
int n = int(8e8) + rand() % int(1e8);
int k = int(8e8) + rand() % int(1e8);
printf("%d %d\n", n, k);
}
题目大意
总共有 n n n个城市,求从 1 1 1号城市出发经过 k k k又回到 1 1 1号城市的方案数(注意:连续两天不能待在同一个城市)
解题思路
设 k k k天后在城市 1 1 1的方案数为 f ( k ) f(k) f(k),前 k − 1 k-1 k−1天每天有 n − 1 n-1 n−1种选择,最后一天必须去城市 1 1 1,即有 ( n − 1 ) k − 1 (n-1)^{k-1} (n−1)k−1种可能,但是这包括了倒数第二天在城市 1 1 1的情况。但我们会发现倒数第二天在城市 1 1 1的方案数即 f ( k − 1 ) f(k-1) f(k−1),即 f ( k ) = ( n − 1 ) k − 1 − f ( k − 1 ) f(k)=(n-1)^{k-1}-f(k-1) f(k)=(n−1)k−1−f(k−1)。
不访列举前几项:
f ( k = 1 ) = 0 f(k=1)=0 f(k=1)=0
f ( k = 2 ) = n − 1 f(k=2)=n-1 f(k=2)=n−1
f ( k = 3 ) = ( n − 1 ) 2 − f ( k = 2 ) = ( n − 1 ) 2 − ( n − 1 ) f(k=3)=(n-1)^2-f(k=2)=(n-1)^2-(n-1) f(k=3)=(n−1)2−f(k=2)=(n−1)2−(n−1)
f ( k = 4 ) = ( n − 1 ) 3 − f ( k − 3 ) = ( n − 1 ) 3 − ( n − 1 ) 2 + ( n − 1 ) f(k=4)=(n-1)^3-f(k-3)=(n-1)^3-(n-1)^2+(n-1) f(k=4)=(n−1)3−f(k−3)=(n−1)3−(n−1)2+(n−1)
可以发现每一项均是等比数列的求和等式,但是 a 2 a_2 a2在 f ( k = 3 ) , f ( k = 4 ) f(k=3),f(k=4) f(k=3),f(k=4)中的值却不一样,刚好是相反数,其它数也是,所以先都以 k k k为偶数的值来求通项式和求和公式,最后若是奇数,取反即可。
于是,进行如下推导
因为当 k = 1 k=1 k=1时, f ( k = 1 ) = 0 f(k=1)=0 f(k=1)=0,而等比数列的首项不能为 0 0 0
所以假设
a
2
a_2
a2为首项,公比
q
=
−
(
n
−
1
)
q=-(n-1)
q=−(n−1)由等比数列可知
a
k
=
a
2
[
−
(
n
−
1
)
]
k
−
1
=
(
−
1
)
k
−
1
(
n
−
1
)
k
S
k
=
a
2
(
1
−
q
k
)
1
−
q
=
a
2
(
1
−
(
−
1
)
k
(
n
−
1
)
k
)
n
=
(
n
−
1
)
−
(
−
1
)
k
(
n
−
1
)
k
+
1
n
\begin{aligned} a_k&=a_2[-(n-1)]^{k-1}\\ &=(-1)^{k-1}(n-1)^k\\ S_k&=\frac{a_2(1-q^k)}{1-q}\\ &=\frac{a_2(1-(-1)^{k}(n-1)^k)}{n}\\ &=\frac{(n-1)-(-1)^{k}(n-1)^{k+1}}{n}\\ \end{aligned}
akSk=a2[−(n−1)]k−1=(−1)k−1(n−1)k=1−qa2(1−qk)=na2(1−(−1)k(n−1)k)=n(n−1)−(−1)k(n−1)k+1
所以加上对
k
k
k的奇偶判断
f
(
k
)
=
(
−
1
)
k
S
k
−
1
=
(
−
1
)
k
(
n
−
1
)
−
(
−
1
)
k
−
1
(
n
−
1
)
k
n
=
(
−
1
)
k
(
n
−
1
)
−
(
−
1
)
2
k
−
1
(
n
−
1
)
k
n
=
(
−
1
)
k
(
n
−
1
)
+
(
n
−
1
)
k
n
\begin{aligned} f(k)&=(-1)^kS_{k-1}\\ &=(-1)^k\frac{(n-1)-(-1)^{k-1}(n-1)^{k}}{n}\\ &=\frac{(-1)^k(n-1)-(-1)^{2k-1}(n-1)^{k}}{n}\\ &=\frac{(-1)^k(n-1)+(n-1)^{k}}{n}\\ \end{aligned}
f(k)=(−1)kSk−1=(−1)kn(n−1)−(−1)k−1(n−1)k=n(−1)k(n−1)−(−1)2k−1(n−1)k=n(−1)k(n−1)+(n−1)k
所以得出求和通项式
f
(
k
)
=
[
(
−
1
)
k
∗
(
n
−
1
)
+
(
n
−
1
)
k
]
/
n
f(k)=[(-1)^k*(n-1)+(n-1)^k]/n
f(k)=[(−1)k∗(n−1)+(n−1)k]/n。
要将这个式子对MOD=998244353取模,需要用到公式a/b%m=a%(b*m)/b。
AC代码
#include <bits/stdc++.h>
const long long mod=998244353;
typedef long long ll;
typedef long long LL;
typedef unsigned long long ull;
using namespace std;
ll M;
//快速乘
LL qmul(LL a,LL b){
LL ret=0;
while(b){
if(b&1) ret=(ret+a)%M;
a=(a+a)%M;
b>>=1;
}
return ret;
}
//快速幂取余
ll q_pow(ll a,ll b)
{
ll ans=1;
while(b)
{
if(b&1)
ans=qmul(ans,a);
a=qmul(a,a);
b>>=1;
}
return ans;
}
int main(int argc, char const *argv[])
{
ll n,k;
cin>>n>>k;
M=n*mod;
ll res;
//套用推出的公式
if(k&1)
res=(1-n+M+q_pow(n-1,k))%M/n;
else
res=(n-1+M+q_pow(n-1,k))%M/n;
cout<<res<<endl;
return 0;
}
总结
当ll q_pow(ll a,ll b,ll mod)
的任意一个形参大于
1
e
9
1e9
1e9的时候,此时函数返回的结果是错的,会炸
l
o
n
g
l
o
n
g
long long
longlong的。所以必须要用到快速乘来解决。要学会反向推结论。