P7481 梦现时刻
P7481 梦现时刻
题目大意
给定 \(n,m\) ,保证 \(m\le n\),令 \(F(a,b)=\sum_{i=0}^{b}\binom{b}{i}\binom{n-i}{a}\)。
求 \(\bigoplus_{a=1}^{m}\bigoplus_{b=1}^{m}(F(a,b) \bmod 998244353)\)。
其中 \(\oplus\) 表示异或运算。
分析
这里说两种DP的办法(其实也能用生成函数,但是太菜了,不会)
其实,这两种方法的根源思想是一样的,讲完之后,我们来说一下。
第一种
我们将\(C(b,i)*C(n-i,a)\),视为从n个球中按两步取球:第一步从前b个球中取出i个球,第二步从剩下的n-i个球中取出a个球。
于是,\(F(a,b)=\sum_{i=1}^{m}C(b,i)*C(n-i,a)\),即为第一步从前b个球中取出若干个球,第二步从剩下的球中取出a个球的取法总数。
我们假设f(i,j)表示第一步从前i个球中取出若干个球,第二步从剩下的球中取出j个球的取法总数,则题意即对于\(1 \leq i \leq m,1 \leq j \leq m\)求出f(i.j)的值。注意此处的i和题目中的i意义并不相同。为什么提出这个状态定义呢?因为当我们将F(a,b)的组合意义提出后,则我们要求的式子其实就是a,b不断变化的F(a,b)的异或值。f(i,j)其中的i,j其实就是F(a,b)中的a,b。
为了方便,我们再设g(i,j)表示第一步从前i个球中取出若干个球,第二步从剩下的求中取出j个球,但不能取第i+1个球的取法总数。
因为,我们动态规划转移时,一般都考虑最后一步不同,我们以第i+1个球是否取来作为转移的标准。
得出递推式:
f(i,j)=f(i−1,j)+g(i−1,j)
其中f(i-1,j)表示第一步不取第i个球(第二步仍然可以取),g(i-1,j)表示第一步取第i个球(第二步就不能取了)。g(i,j)=f(i,j)-g(i,j-1)
其中f(i,j)代表第二步取不取第i+1个球无所谓,g(i,j-1)代表第二步取第i+1个球(于是只能从剩下的球中取出j-1个,且不能取第i+1个球)。
边界条件:\(f(0,i)=C(n,i),f(i,0)=2^i,g(0,i)=C(n-1,j),g(i,0)=2^i\)
待会再看代码。
第二种
第二种就更奇妙了,疯狂利用基础的恒等式\(C(n,m) = C(n-1,m-1) + C(n-1,m)\)。
来欣赏一下推导过程。
对于后面的式子,发现其与\(\sum_{i=0}^{b}C(b,i)*C(n-i-1,a)\)相似
我们逆推回去。
带回刚才推出的式子中。
边界条件:\(F(0,k)=\sum_{i=0}^{k}C(k,i),F(k,0)=C(n,k)\)。
组合数也不难算。
现在我们来说说为什么两种思路根源想法相同,其实都是利用了,最后一步不同进行拆分。
AC_code
第一种
#include<bits/stdc++.h>
#define ios ios::sync_with_stdio(false); cin.tie(0), cout.tie(0)
using namespace std;
const int N = 5e3 + 10,mod = 998244353;
template<int T>
struct ModInt {
const static int mod = T;
int x;
ModInt(int x = 0) : x(x % mod) {}
int val() { return x; }
ModInt operator + (const ModInt &a) const { int x0 = x + a.x; if (x0 >= mod) x0 -= mod; if (x0 < 0) x0 += mod; return ModInt(x0); }
ModInt operator - (const ModInt &a) const { int x0 = x - a.x; if (x0 >= mod) x0 -= mod; if (x0 < 0) x0 += mod; return ModInt(x0); }
ModInt operator * (const ModInt &a) const { return ModInt(1LL * x * a.x % mod); }
ModInt operator / (const ModInt &a) const { return *this * a.inv(); }
void operator += (const ModInt &a) { x += a.x; if (x >= mod) x -= mod; if (x < 0) x += mod;}
void operator -= (const ModInt &a) { x -= a.x; if (x < 0) x += mod; if (x >= mod) x -= mod;}
void operator *= (const ModInt &a) { x = 1LL * x * a.x % mod; }
void operator /= (const ModInt &a) { *this = *this / a; }
friend ostream &operator<<(ostream &os, const ModInt &a) { return os << a.x;}
ModInt pow(int n) const {
ModInt res(1), mul(x);
while(n){
if (n & 1) res *= mul;
mul *= mul;
n >>= 1;
}
return res;
}
ModInt inv() const {
int a = x, b = mod, u = 1, v = 0;
while (b) {
int t = a / b;
a -= t * b; swap(a, b);
u -= t * v; swap(u, v);
}
if (u < 0) u += mod;
return u;
}
};
typedef ModInt<mod> mint;
int main()
{
ios;
int n,m;
cin>>n>>m;
vector<vector<mint>> f(m+1,vector<mint>(m+1)),g(m+1,vector<mint>(m+1));
vector<mint> inv(m+1);
for(int i=1;i<=m;i++) inv[i] = mint(i).inv();
f[0][0] = 1;
for(int i=1;i<=m;i++)
{
f[0][i] = f[0][i-1]*mint(n-i+1)*inv[i];
f[i][0] = f[i-1][0]*mint(2);
}
g[0][0] = 1;
for(int i=1;i<=m;i++)
{
g[0][i] = g[0][i-1]*mint(n-i)*inv[i];
g[i][0] = g[i-1][0]*mint(2);
}
int ans = 0;
for(int i=1;i<=m;i++)
for(int j=1;j<=m;j++)
{
f[i][j] = g[i-1][j]+f[i-1][j];
g[i][j] = f[i][j]-g[i][j-1];
ans ^= f[i][j].val();
}
cout<<ans<<'\n';
return 0;
}
第二种
#include<bits/stdc++.h>
#define ios ios::sync_with_stdio(false); cin.tie(0), cout.tie(0)
using namespace std;
const int N = 5e3 + 10,mod = 998244353;
template<int T>
struct ModInt {
const static int mod = T;
int x;
ModInt(int x = 0) : x(x % mod) {}
int val() { return x; }
ModInt operator + (const ModInt &a) const { int x0 = x + a.x; if (x0 >= mod) x0 -= mod; if (x0 < 0) x0 += mod; return ModInt(x0); }
ModInt operator - (const ModInt &a) const { int x0 = x - a.x; if (x0 >= mod) x0 -= mod; if (x0 < 0) x0 += mod; return ModInt(x0); }
ModInt operator * (const ModInt &a) const { return ModInt(1LL * x * a.x % mod); }
ModInt operator / (const ModInt &a) const { return *this * a.inv(); }
void operator += (const ModInt &a) { x += a.x; if (x >= mod) x -= mod; if (x < 0) x += mod;}
void operator -= (const ModInt &a) { x -= a.x; if (x < 0) x += mod; if (x >= mod) x -= mod;}
void operator *= (const ModInt &a) { x = 1LL * x * a.x % mod; }
void operator /= (const ModInt &a) { *this = *this / a; }
friend ostream &operator<<(ostream &os, const ModInt &a) { return os << a.x;}
ModInt pow(int n) const {
ModInt res(1), mul(x);
while(n){
if (n & 1) res *= mul;
mul *= mul;
n >>= 1;
}
return res;
}
ModInt inv() const {
int a = x, b = mod, u = 1, v = 0;
while (b) {
int t = a / b;
a -= t * b; swap(a, b);
u -= t * v; swap(u, v);
}
if (u < 0) u += mod;
return u;
}
};
typedef ModInt<mod> mint;
mint fact[N],infact[N];
void init()
{
fact[0] = infact[0] = 1;
for(int i=1;i<N;i++) fact[i] = fact[i-1]*mint(i);
infact[N-1] = fact[N-1].inv();
for(int i=N-2;i;i--) infact[i] = infact[i+1]*mint(i+1);
}
mint C(int a,int b)
{
return fact[a]*infact[a-b]*infact[b];
}
mint get1(int b)
{
mint res = 0;
for(int i=0;i<=b;i++) res += C(b,i);
return res;
}
mint get2(int n,int b)
{
mint res = 1;
for(int i=n;i>=n-b+1;i--) res *= mint(i);
res *= infact[b];
return res;
}
int main()
{
ios;
init();
int n,m;cin>>n>>m;
vector<vector<mint>> f(m+1,vector<mint>(m+1));
f[0][0] = 1;
for(int i=1;i<=m;i++) f[0][i] = get1(i),f[i][0] = get2(n,i);
int ans = 0;
for(int i=1;i<=m;i++)
for(int j=1;j<=m;j++){
f[i][j] = mint(2)*f[i][j-1] + f[i-1][j-1] - f[i-1][j];
ans ^= f[i][j].val();
}
cout<<ans<<'\n';
return 0;
}

浙公网安备 33010602011771号