LOADING

加载过慢请开启缓存 浏览器默认开启

codeforces136C

这波是划水叶师傅对逆元有感而发,觉得有必要好好学学,组合数开mod还是挺有必要的

有一说一,逆元是从沈✌那学的,逆元的证明看右边分栏里 s师傅的blog里的数论分块,我就不写了

题意:

有n张标号 1~n 的牌(保证为偶数),Alice和Bob每人分到 n/2 张牌进行游戏。
游戏规则为:
Alice先手出一张牌,若Bob不能出比Alice的更大的牌则Bob输了,反之两张牌去除,一回合结束。下一回合由Bob先手,持续到一方没牌时平局。
问 Alice 赢,Bob 赢 ,二人平局的情况分别有多少种。
某个朋友一眼博弈论,在此我要狠狠的批评TA,我每场前都提醒TA,但是TA每场都不打!

当 n = 4 的时候,每人两张牌的话一共有 = 6 种,其中
当Alice的牌为{1,4},{2,4},{3,4}时必胜
当Alice的牌为{1,3},{1,2}时Bob必胜
当Alice的牌为{2,3}是平局(Alice先手出3,Bob出4,Bob出1,Alice出2,此时二者都没牌)

我们用a[i]记录Alice 在 n=i 时赢的情况,b[i] 记录 Alice输的情况(即Bob赢)
我们可以随便取一个值自己从后往前推一下发现,平局的情况是固定的且只有一种

我们来看当 n=6 时,
第一种情况,我们可以发现如果Alice选第6个牌的话,其他两个牌不论这么选都能赢(因为先手)
共有 = 10 种情况

第二种情况,如果Alice不选6,Bob选6,而Alice选5,那么在第一回合5和6都会被用掉,只剩下前四张且Bob先手出牌 ,如果在这四张牌里Bob先手且要Alice赢的话,我们需要知道4张牌时先手必败的情况,这我们就可以从 b[4] 里知道,从前 for(int i=4;i<=60;i+=2) 往后计算,每一个b[i-2]都存在,所以此时的 a[6]=+b[4]=12 , b[6]=-a[6]-1
综上所述,每一次的 a[i]=+b[i-2] , b[i]= -a[i]-1

接下来就是代码的解决了
我们发现n最大有60,组合数都爆范围了,所以题目也要求要MOD,此时就需要用逆元来求组合数,

对 1到60的逆元 我们可以用递推公式求得 证明请看沈师傅

for(int i=1;i<=60l++i){
  inv[i]=(MOD-MOD/i)*inv[MOD%i]%MOD;
}

此时inv数组存着每一个数的逆元

当我们算 时,简单算一下发现
分子要计算 i 到 (i/2+1) 分母要计算 (i/2) 到 1,分别有 i/2 个

这时候就可以从大往小 *分母 *分子的逆元 (MOD)。

    for(int x=i,y=i/2;y>0;--x,--y){
        num=((num*x)%MOD*inv[y])%MOD;
    }

也是同理
code:

#include <bits/stdc++.h>
#define rep(i,n,m) for(int i=n;i<=m;++i)
#define per(i,n,m) for(int i=n;i>=m;--i)
#define PII pair<int,int>
#define lowbit(x) x&-x
#define INF 1<<30
using namespace std;
typedef long long ll;
const int N=2e5+10;
const int MOD=998244353;
ll inv[N],a[N],b[N];
int main()
{
    ios::sync_with_stdio(0);cin.tie(0);
    //初始化
  inv[1]=1,a[2]=1,b[2]=0;
    
  //计算逆元
    rep(i,2,60)inv[i]=(MOD-MOD/i)*inv[MOD%i]%MOD;

    for(int i=4;i<=60;i+=2){
        //计算第一种情况时的组合数
    ll sum=1;
        for(int x=i-1,y=i/2-1;y>0;--x,--y){
            sum=((sum*x)%MOD*inv[y])%MOD;
        }
    //计算第二种情况时的组合数
        ll num=1;
        for(int x=i,y=i/2;y>0;--x,--y){
            num=((num*x)%MOD*inv[y])%MOD;
        }

    //因为前面MOD过了,数据可能是负数,请记得 +MOD
        a[i]=(sum+b[i-2]+MOD)%MOD;
        b[i]=(num-a[i]-1+MOD)%MOD;
    }

    int m;cin>>m;
    while(m--){
        int n;cin>>n;
        cout<<a[n]<<" "<<b[n]<<' '<<1<<endl; 
    }
    return 0;
}

其实有时间的话,自己一个一个打表也行的(doge)