题目链接
参考博客
你在一个有 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 |
输出 |
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。
此处贴一个动态规划思路
#include
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的。所以必须要用到快速乘来解决。要学会反向推结论。