牛客小白赛14 A.简单计数

题目链接

参考博客

题目描述

你在一个有 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 k1天每天有 n − 1 n-1 n1种选择,最后一天必须去城市 1 1 1,即有 ( n − 1 ) k − 1 (n-1)^{k-1} (n1)k1种可能,但是这包括了倒数第二天在城市 1 1 1的情况。但我们会发现倒数第二天在城市 1 1 1的方案数即 f ( k − 1 ) f(k-1) f(k1),即 f ( k ) = ( n − 1 ) k − 1 − f ( k − 1 ) f(k)=(n-1)^{k-1}-f(k-1) f(k)=(n1)k1f(k1)

不访列举前几项:

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)=n1

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)=(n1)2f(k=2)=(n1)2(n1)

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)=(n1)3f(k3)=(n1)3(n1)2+(n1)

可以发现每一项均是等比数列的求和等式,但是 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=(n1)由等比数列可知
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[(n1)]k1=(1)k1(n1)k=1qa2(1qk)=na2(1(1)k(n1)k)=n(n1)(1)k(n1)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)kSk1=(1)kn(n1)(1)k1(n1)k=n(1)k(n1)(1)2k1(n1)k=n(1)k(n1)+(n1)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(n1)+(n1)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的。所以必须要用到快速乘来解决。要学会反向推结论。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值