题意是给你n张牌,序号是1到n,然后每一轮各出一张(不放回),直到没有更大的牌为止。每一轮先手交换,问你最后谁赢
博弈论一般先想决策,然后想必胜态或者必输态,然后开始继续考虑
这个题的决策是:
两堆牌,先手出最大的牌,后者出他那堆比出的这个牌大的最小的牌
知道了这个就好做了
为什么是这个决策呢:
先手必定是想让自己赢的,所以牌如果取小了,必输,所以取一张自己所拥有的最大的牌。然后后者想要不输,必须找一张更大的,但是不能用最大的,因为后面还有轮数要进行
所以这里思路来了:
把牌分成两堆,令f[i]是牌数先手必胜的方案数
先手必胜有两种状态:
①有此时最大的牌(i*2),此时先手必胜
②此时先手有(2*i-1)的牌,后手有(2*i)的牌,这个时候两个牌相互抵消。然后下一轮应该是后者轮了,这个时候应该是后者轮的先手输,才会保证本来的先手必胜
总的来说:本局的先手必胜态=本局的必胜+上一局的先手必胜,然后递推即可
下面看代码:
实现用组合数,只讲了思路
#pragma GCC optimize(1)
#pragma GCC optimize(2)
#pragma GCC optimize(3,"Ofast","inline")
#define IOS ios::sync_with_stdio(false), cin.tie(0);
#include<iostream>
#include<map>
#include<set>
#include<cstdio>
#include<cstring>
#include<vector>
#include<stack>
#include<algorithm>
#include<cmath>
#include<queue>
#include<deque>
using namespace std;
#define int long long
typedef long long ll;
typedef pair<int,int> PAII;
const int N=2e6+10,M=5050,INF=1e18,mod=998244353;
int f[M],c[M][M];
int n;
void init()
{
for (int i = 0; i < 200; i ++ )
for (int j = 0; j <= i; j ++ )
if (!j) c[i][j] = 1;
else c[i][j] = (c[i - 1][j] + c[i - 1][j - 1]) % mod;
}
signed main(){
IOS;
int T;
//T=1;
cin>>T;
init();
f[1]=1;
for(int i=2;i<=30;i++)
f[i]=(c[2*i-1][i-1] + (c[(i-1)*2][i-1] - f[i-1]-1)+mod)%mod;//递推:这一轮必胜态+上一轮的必胜态
while(T--)
{
cin>>n;
n/=2;
cout<<f[n]<<" "<<(c[n*2][n] - f[n] - 1+mod)%mod<<" "<<"1\n";//负数取模
}
return 0;
}
/*
*/