solution-at-agc040-f

orz kkio

计数题,考虑怎么去重。发现有可能的重复方案是靠后的那个棋子往前走了一步,或者选择进行一次操作2,由于一个方案相同当且仅当两个棋子在每一步的坐标都是相同的,而不是操作不同,所以上面那种情况就会算重。

注意到两个棋子之间没有区别,所以考虑几何意义。考虑靠后的棋子向前移动一步相当于沿 $y$ 轴移动一步,靠前的棋子向前移动一步相当于沿 $x$ 轴正半轴往前移动一步。于是,题目变成了从原点出发,有三种操作:

  1. 朝 $x$ 轴正半轴方向走一步
  2. 朝 $y$ 轴正半轴方向走一步
  3. 设当前位置为 $(x_0,y_0)$ 把当前位置挪到 $(x_0,x_0)$

其中,操作1和操作2需满足除原点外不可以移动后经过/处于 $y=x$ 这条直线上。问你用 $n$ 步从原点走到点 $(B,A)$ 的方案数。

这样子就不会算重了,因为上述有可能的重复方案都只会选择一种操作3.

接下来考虑怎么计数。首先不考虑操作3,相当于一个路径计数问题。假设用 $x+y$ 次操作1和操作2从原点走到点 $(B,k)$ 则方案数容斥一下即为 ${B + k - 1\choose B - 1} - {B + k - 1\choose k - 1}$.这时候,只有 $k = A$ 方案数合法的。

考虑操作3。第 $i$ 次操作后,当前点坐标 $(x_i,y_i)$,我们设 $d_i = x_i - y _ i$.显然在不考虑方案3的情况下, $d_i$ 是连续的。考虑一下什么在哪一次操作12前我们可以塞进一个操作3而导致最后的纵坐标变大。显然这次操作后,后面的所有点也必须满足不能碰到直线 $y=x$.有了这个限制后,容易发现我们貌似只能在每一个 $i$ 满足 $d_i$ 为 $d_i$ 这个值最后一次出现在这个序列 $d$ 中,由于$d$ 满足是连续的,即 $\forall j > i$ 满足 $d_j > d_i$ .

再观察一下,发现对于从原点到点 $(B,k)$ 的方案,最后一次操作3必然放在 $d_i = A - k$ 上面。于是问题变成了要把剩下的操作3塞进 $[0,A-k-1]$ 这个区间的可行操作12之前。插板一下就行了

// Problem: [AGC040F] Two Pieces
// Contest: Luogu
// URL: https://www.luogu.com.cn/problem/AT_agc040_f
// Memory Limit: 1 MB
// Time Limit: 4000 ms
// 
// Powered by CP Editor (https://cpeditor.org)

#include<bits/stdc++.h>
using namespace std;
const long long inf = 1e18;
const int mininf = 1e9 + 7;
#define int long long
#define pb emplace_back
inline int read(){int x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}return x*f;}
inline void write(int x){if(x<0){x=~(x-1);putchar('-');}if(x>9)write(x/10);putchar(x%10+'0');}
#define put() putchar(' ')
#define endl puts("")
const int mod = 998244353;
const int MAX = 1e7 + 10;

int pre[MAX];
int inv[MAX];
int f[MAX];
int c2(int n, int m){
    if(m > n)   return 0;
    return pre[n] * f[m] % mod * f[n-m] % mod;
}

int calc(int x, int y){
    if(y == 0)  return 1;
    return (c2(x + y - 1, x - 1) - c2(x + y - 1, y - 1) % mod + mod) % mod;
}

void solve(){
    int n = read(), A = read(), B = read(); // B > A
    int ans = 0;
    for(int k = 0; k <= min(A, n - B); k++){
        int ret = calc(B, k);
        if(n - k - B == 0){
            if(k == A)  ans += ret, ans %= mod;
        }else{
            ans += ret * c2(A - k + 1 + n - B - k - 2, n - B - k - 1) % mod;
            ans %= mod;
        }
    }
    write(ans), endl;
}

signed main(){
     pre[0] = f[0] = 1;
    pre[1] = inv[1] = f[1] = 1;
    for(int i = 2; i < MAX; i++){
        pre[i] = 1ll * pre[i - 1] * i % mod;
        inv[i] = 1ll * inv[mod % i] * (mod - mod / i) % mod;
        f[i] = 1ll * f[i - 1] * inv[i] % mod;
    }
    int t = 1;
    while(t--)  solve();
    return 0;
}
posted @ 2023-12-12 17:26  WRuperD  阅读(4)  评论(0编辑  收藏  举报  来源

本文作者:DIVMonster

本文链接:https://www.cnblogs.com/guangzan/p/12886111.html

版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。

这是一条自定义内容

这是一条自定义内容