题目大意
像走象棋,棋盘有两行,很多列,第一列上有 nnn 个炮, 第二列上有 mmm 个炮,炮的规则就和象棋一样,可以隔一个打一个。现在有两种规则,对于每一种规则你需要回答一个数。
规则一:
你可以任意地下象棋,定义 fif_ifi 表示你下 iii 步之后可能形成的局面
求 f0 xor f1 xor … xorfn+m−4f_0 \ xor \ f_1 \ xor \ … \ xor f_{n+m-4}f0 xor f1 xor … xorfn+m−4 的值
规则二:
你在下象棋的时候下过第二行之后就不能下第一行了,同样求 f0 xor f1 xor … xorfn+m−4f_0 \ xor \ f_1 \ xor \ … \ xor f_{n+m-4}f0 xor f1 xor … xorfn+m−4 的值
解题思路
我们首先将 n=n−2 m=m−2n = n-2 \ \ \ m = m-2n=n−2 m=m−2
在一行有 nnn 个炮进行一步操作的局面有 2×(n−2)2\times (n-2)2×(n−2) 种,可以这样考虑有 n−2n-2n−2 个炮可以向右移动,有 n−2n-2n−2个炮可以向左移动。
所以我们在一行有 nnn 个炮进行 kkk 步操作的结果有
2k×Cnk×Akk2^k \times C^k_n \times A^k_k2k×Cnk×Akk
规则一
所以对于第一种操作,我们进行 kkk 次操作可能形成的局面有
∑i=0kCki×2i×Cni×Aii×2k−i×Cmk−i×Ak−ik−i\sum_{i=0}^k C_k^i\times2^i\times C^i_n\times A_i^i\times2^{k-i}\times C^{k-i}_m \times A_{k-i}^{k-i}i=0∑kCki×2i×Cni×Aii×2k−i×Cmk−i×Ak−ik−i
化简一下
2k×k!×∑i=0k×Cni×Cmk−i2^k \times k! \times \sum_{i=0}^k\times C_n^{i} \times C_m^{k-i}2k×k!×i=0∑k×Cni×Cmk−i
由组合数的意义,我们化简一下求和式
2k×k!×Cn+mk2^k \times k! \times C_{n+m}^{k}2k×k!×Cn+mk
所以我们对于规则一的答案就是
对于 k∈[0,m+n]k \in [0, m+n]k∈[0,m+n] 将 上式相异或
规则二
进行 kkk 此操作可以形成的局面是
∑i=0k2i×Cni×Aii×2k−i×Cmk−i×Ak−ik−i\sum_{i=0}^k 2^i\times C^i_n\times A_i^i\times2^{k-i}\times C^{k-i}_m \times A_{k-i}^{k-i}i=0∑k2i×Cni×Aii×2k−i×Cmk−i×Ak−ik−i
化简一下
2k∑i=0kn!(n−i)!×m!(m−k+i)!2^k \sum_{i=0}^k \frac{n!}{(n-i)!}\times\frac{m!}{(m-k+i)!}2ki=0∑k(n−i)!n!×(m−k+i)!m!
我们上下同时乘以(m+n−k)!(m+n-k)!(m+n−k)!得到
2k×n!×m!(n+m−k)!∑i=0kCn+m−kn−i2^k \times \frac{n! \times m!}{(n+m-k)!} \sum_{i=0}^k C_{n+m-k}^{n-i}2k×(n+m−k)!n!×m!i=0∑kCn+m−kn−i
为了后面计算方便,我们将求和式转化一下
2k×n!×m!(n+m−k)!∑i=n−knCn+m−ki2^k \times \frac{n! \times m!}{(n+m-k)!} \sum_{i=n-k}^n C_{n+m-k}^{i}2k×(n+m−k)!n!×m!i=n−k∑nCn+m−ki
我们发现,求和式是一个组合数的前缀和之差
定义S(n,m)=∑i=0mCniS(n, m) = \sum_{i=0}^mC_n^iS(n,m)=i=0∑mCni
用如下公式来求前缀和
S(n,m+1)=S(n,m)+Cnm+1S(n,m+1) = S(n,m) + C_n^{m+1}S(n,m+1)=S(n,m)+Cnm+1
S(n,m−1)=S(n,m)−CnmS(n, m-1) = S(n, m) - C_n^mS(n,m−1)=S(n,m)−Cnm
S(n+1,m)=2S(n,m)−CnmS(n+1, m) = 2S(n,m) - C_n^mS(n+1,m)=2S(n,m)−Cnm
我们将求和式转化一下
∑i=n−knCn+m−ki=∑i=0nCn+m−ki−∑i=0n−k−1Cn+m−ki\sum_{i=n-k}^n C_{n+m-k}^{i} = \sum_{i=0}^n C_{n+m-k}^{i} - \sum_{i=0}^{n-k-1} C_{n+m-k}^{i}i=n−k∑nCn+m−ki=i=0∑nCn+m−ki−i=0∑n−k−1Cn+m−ki
我们在计算的时候开一个数组s1[k]s1[k]s1[k] 表示∑i=0nCn+m−ki\sum_{i=0}^n C_{n+m-k}^{i}∑i=0nCn+m−ki
显然s1[n+m]=1s1[n+m] = 1s1[n+m]=1,我们可以这样逆向递推
s1[i−1]=2×s1[i]−Cn+m−ins1[i-1] = 2\times s1[i] - C_{n+m-i}^ns1[i−1]=2×s1[i]−Cn+m−in
同时开另外一个数组s2[k]s2[k]s2[k] 表示 ∑i=0n−k−1Cn+m−ki\sum_{i=0}^{n-k-1} C_{n+m-k}^{i}∑i=0n−k−1Cn+m−ki
显然s2[n−1]=1s2[n-1] = 1s2[n−1]=1,我们可以这样逆向递推
s2[i−1]=2×s2[i]+Cn+m−in−is2[i-1] = 2\times s2[i] + C_{n+m-i}^{n-i}s2[i−1]=2×s2[i]+Cn+m−in−i
然后我们将 s1s1s1 和 s2s2s2 作差就能得到我们想要的求和式了
至此,第二步也结束了。
Code
#include <bits/stdc++.h>
#define ll long long
#define qc ios::sync_with_stdio(false); cin.tie(0);cout.tie(0)
#define fi first
#define se second
#define PII pair<int, int>
#define PLL pair<ll, ll>
#define pb push_back
using namespace std;
const int MAXN = 1e7 + 7;
const int inf = 0x3f3f3f3f;
const ll INF = 0x3f3f3f3f3f3f3f3f;
const ll mod = 1e9 + 9;
inline int read()
{
int x=0,f=1;char ch=getchar();
while (!isdigit(ch)){if (ch=='-') f=-1;ch=getchar();}
while (isdigit(ch)){x=x*10+ch-48;ch=getchar();}
return x*f;
}
int f[MAXN];
int f2[MAXN];
int finv[MAXN];
int s1[MAXN], s2[MAXN];
ll n, m;
ll C(ll n, ll m){
if(m > n)
return 0;
return 1ll * f[n] * finv[n-m] % mod * finv[m] % mod;
}
void solve(){
cin >> n >> m;
n -= 2;
m -= 2;
s1[n+m] = 1;
for(int i = n+m; i > 0; --i){
s1[i-1] = (2ll * s1[i] % mod - C(n+m-i, n) + mod) % mod;
}
s2[n-1] = 1;
for(int i = n-1; i > 0; --i){
s2[i-1] = (2ll * s2[i] % mod + C(n + m - i, n - i)) % mod;
}
for(int i = 0; i <= n+m; ++i)
s1[i] = (1ll * s1[i] - s2[i] + mod) % mod;
ll ans1 = 0, ans2 = 0;
for(int i = 0; i <= n+m; ++i){
ans1 ^= 1ll * f2[i] * f[i] % mod * C(n+m, i) % mod;
ans2 ^= 1ll * f2[i] * s1[i] % mod * f[n] % mod * f[m] % mod * finv[n+m-i] % mod;
}
cout << ans1 << " " << ans2 << endl;
}
int main()
{
#ifdef ONLINE_JUDGE
#else
freopen("in.txt", "r", stdin);
freopen("out.txt", "w", stdout);
#endif
finv[1] = 1;
finv[0] = 1;
for(int i = 2; i < MAXN; ++i)
finv[i] = 1ll * (mod - mod / i ) * 1ll * finv[mod % i] % mod;
f[0] = 1;
for(ll i = 1; i < MAXN; i++){
f[i] = f[i-1] * i % mod;
finv[i] = 1ll * finv[i] * finv[i-1] % mod;
}
f2[0] = 1;
for(ll i = 1; i < MAXN; i++)
f2[i] = f2[i-1] * 2ll % mod;
// for(int i = 1; i <= 100; i++)
// cout << finv[i] << " === " << qpow(f[i], mod-2) << endl;
qc;
int T;
// cin >> T;
T = 1;
while(T--){
solve();
}
return 0;
}
Tips
- 数组开long longlong \ longlong long会爆空间,所以我们要边用边开
- 数据量是 5e65e65e6 所以复杂度可能只支持 O(n+m)O(n+m)O(n+m) 很多东西要预处理出来
本文探讨了象棋棋盘上的炮规则问题,规则一是任意下棋,规则二是限制行次。通过计算每一步操作后的可能状态数量,分别求得两种规则下f0异或...的值,涉及组合数和递推算法。
264

被折叠的 条评论
为什么被折叠?



