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个球是否取来作为转移的标准。

得出递推式:

  1. f(i,j)=f(i−1,j)+g(i−1,j)
    其中 f(i-1,j) 表示第一步不取第 i 个球(第二步仍然可以取),g(i-1,j) 表示第一步取第 i个球(第二步就不能取了)。
  2. 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)\)

来欣赏一下推导过程。

\[F(a,b+1) = \sum_{i=0}^{b+1}C(b+1,i)*C(n-i,a)\\ =\sum_{i=0}^{b+1}C(b,i)*C(n-i,a)+\sum_{i=0}^{b+1}C(b,i-1)*C(n-i,a)\\ =F(a,b)+\sum_{i=0}^{b+1}C(b,i-1)*C(n-i,a)\\ =\sum_{i=1}^{b+1}C(b+1,i)*C(n-i,a) + F(a,b)\\ =\sum_{i=0}^{b}C(b,i)*C(n-i-1,a)+F(a,b)\\ =\sum_{i=0}^{b}C(b,i)*C(n-i,a)-\sum_{i=0}^{b}C(b,i)*C(n-i-1,a)+F(a,b)\\ =2F(a,b)-\sum_{i=0}^{b}C(b,i)*C(n-i-1,a) \]

对于后面的式子,发现其与\(\sum_{i=0}^{b}C(b,i)*C(n-i-1,a)\)相似

我们逆推回去。

\[\sum_{i=0}^{b}C(b,i)*C(n-i-1,a-1)\\ =\sum_{i=1}^{b+1}C(b,i-1)*C(n-i,a-1)\\ =\sum_{i=1}^{b+1}C(b+1,i)*C(n-i,a-1)-\sum_{i=1}^{b+1}C(b,i)*C(n-i,a-1)\\ =F(a-1,b+1)-\sum_{i=1}^{b}C(b+1,i)*C(n-i,a-1)\\ =F(a-1,b+1)-F(a-1,b) \]

带回刚才推出的式子中。

\[F(a,b+1) = 2F(a,b)-F(a-1,b+1)+F(a-1,b)\\ F(a,b) = 2F(a,b-1)-F(a-1,b)+F(a-1,b-1) \]

边界条件:\(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;
}
posted @ 2022-09-19 00:16  艾特玖  阅读(49)  评论(0)    收藏  举报