[ARC124F]Chance Meeting
壹、题目描述 ¶
贰、题解 ¶
相当于把题解翻译一遍了......
将两个东西相碰转化为,在某一时刻两个人所处行列均相同。接下来,出现的 \(n,m\) 均为原题目减一(由行列长度变成了步数)
注意到无论两个东西怎么走,在经过了 \(n\) 次竖直操作之后,他们俩必定在同一行,因而,竖直操作的细节以并不是那么重要,我们只需要考虑他们在第 \(n\) 行相遇的情形即可(其他情况是完全一样的)。
接下来,我们假设 \(f(i)\) 表示两个东西在 \((n, i)\) 第一次相遇的方案数,不难发现总的答案即
因为从相遇点 \((n,i)\) 走到各自终点,实际上就是一个对称。
现在,我们希望能比较快速地求出 \(f(i)\) 的值,然后用己就可以得到答案。
事实上,我们发现它的限制 “第一次” 很烦人,我们尝试使用容斥将其解决掉。
考虑设 \(g(i)\) 表示两个人在 \((n, i)\) 相遇(并不一定第一次)的方案数,那么显然有
组合意义很简单吧?
然后,我们考察 \(f(i)\) 与 \(g(i)\) 的关系,显然会有 \(f(i)=g(i)-C\),其中 \(C\) 我们目前并不确定。
显然,在 \(g(i)\) 中,多出了那些在 \((n, i)\) 之前即相遇的方案数,我们不妨枚举他们倒数第二次相遇在 \((n,k)\) 的方案,然后从 \(g(i)\) 中减掉,那么就有
至于这个式子是什么意思呢?由于它们倒数第二次在 \((n, k)\) 相遇,那么,他们一起,从 \((n, k)\) 走到 \((n, i)\) 的过程中,一定是一个人走在前面,另一个走在后面,并且不能出现 “超车” 的现象,这挺像卡塔兰数,事实上它就是卡塔兰数......为什么有个 \(2\),因为每个人都有机会走在前面。
然后,大家一起卷一卷就好了。
时间复杂度 \(\mathcal O(m\log m)\). 不过跑得好像挺慢,是我常数的原因?鬼知道,反正用 vector
实现很慢就是了。
叁、参考代码 ¶
#include<cstdio>
#include<algorithm>
#include<vector>
#include<cstring>
using namespace std;
#define NDEBUG
#include<cassert>
namespace Elaina{
#define rep(i, l, r) for(int i=(l), i##_end_=(r); i<=i##_end_; ++i)
#define drep(i, l, r) for(int i=(l), i##_end_=(r); i>=i##_end_; --i)
#define fi first
#define se second
#define mp(a, b) make_pair(a, b)
#define Endl putchar('\n')
#define mmset(a, b) memset(a, b, sizeof a)
// #define int long long
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> pii;
typedef pair<ll, ll> pll;
template<class T>inline T fab(T x){ return x<0? -x: x; }
template<class T>inline void getmin(T& x, const T rhs){ x=min(x, rhs); }
template<class T>inline void getmax(T& x, const T rhs){ x=max(x, rhs); }
template<class T>inline T readin(T x){
x=0; int f=0; char c;
while((c=getchar())<'0' || '9'<c) if(c=='-') f=1;
for(x=(c^48); '0'<=(c=getchar()) && c<='9'; x=(x<<1)+(x<<3)+(c^48));
return f? -x: x;
}
template<class T>inline void writc(T x, char s='\n'){
static int fwri_sta[1005], fwri_ed=0;
if(x<0) putchar('-'), x=-x;
do fwri_sta[++fwri_ed]=x%10, x/=10; while(x);
while(putchar(fwri_sta[fwri_ed--]^48), fwri_ed);
putchar(s);
}
}
using namespace Elaina;
const int mod=998244353;
const int primitive_root=3;
const int inverse_primitive_root=332748118;
const int maxn=2e5*3;
inline int qkpow(int a, int n){
int ret=1;
for(; n>0; n>>=1, a=1ll*a*a%mod)
if(n&1) ret=1ll*ret*a%mod;
return ret;
}
namespace NTT{
int G[2][55], invn, n;
vector<int>rev;
inline void initial(){
for(int j=1; j<=50; ++j){
G[0][j]=qkpow(primitive_root, (mod-1)/(1<<j));
G[1][j]=qkpow(inverse_primitive_root, (mod-1)/(1<<j));
}
}
inline void prepare(int len){
for(n=1; n<len; n<<=1);
invn=qkpow(n, mod-2);
rev.resize(n);
for(int i=0; i<n; ++i)
rev[i]=(rev[i>>1]>>1)|((i&1)? (n>>1): 0);
}
inline void ntt(vector<int>&f, int opt){
f.resize(n);
for(int i=0; i<n; ++i) if(i<rev[i])
swap(f[i], f[rev[i]]);
for(int p=2, level=1; p<=n; p<<=1, ++level){
int w=G[opt][level], len=(p>>1);
for(int k=0; k<n; k+=p){
int buf=1, tmp;
for(int i=k; i<k+len; ++i, buf=1ll*buf*w%mod){
tmp=1ll*f[i+len]*buf%mod;
f[i+len]=(f[i]+mod-tmp)%mod;
f[i]=(f[i]+tmp)%mod;
}
}
}
if(opt==1) for(int i=0; i<n; ++i)
f[i]=1ll*f[i]*invn%mod;
}
}
int finv[maxn+5], fac[maxn+5];
inline void prelude(){
fac[0]=1;
for(int i=1; i<=maxn; ++i)
fac[i]=1ll*fac[i-1]*i%mod;
finv[maxn]=qkpow(fac[maxn], mod-2);
for(int i=maxn-1; i>=1; --i)
finv[i]=1ll*finv[i+1]*(i+1)%mod;
finv[0]=1;
}
inline int C(int n, int m){
if(n<m) return 0;
return 1ll*fac[n]*finv[m]%mod*finv[n-m]%mod;
}
int n, m;
vector<int>g, c, f;
signed main(){
prelude(); NTT::initial();
n=readin(1)-1, m=readin(1)-1;
g.resize(m+1), c.resize(m+1);
for(int i=0; i<=m; ++i)
g[i]=1ll*C(2*i+n, 2*i)*C(2*i, i)%mod;
for(int i=0; i<=m; ++i)
c[i]=1ll*C(2*i, i)*finv[i+1]%mod*fac[i]%mod;
NTT::prepare((m+1)<<1);
NTT::ntt(g, 0), NTT::ntt(c, 0);
for(int i=0; i<NTT::n; ++i)
c[i]=1ll*c[i]*g[i]%mod;
NTT::ntt(g, 1), NTT::ntt(c, 1);
g.resize(m+1), c.resize(m+1), f.resize(m+1);
for(int i=1; i<=m; ++i)
f[i]=(0ll+g[i]+mod-2ll*c[i-1]%mod)%mod;
f[0]=1;
int ans=0;
for(int i=0; i<=m; ++i)
ans=(ans+1ll*f[i]*f[m-i]%mod)%mod;
ans=1ll*ans*C(2*n, n)%mod;
writc(ans);
return 0;
}
肆、关键之处 ¶
能发现他们在哪一行相遇都不重要这一点是最关键的,因为它移除了一个无关的变量 \(n\),这样能够让我们更好地处理相遇。
另,计算方案时不用过于着急列出包含所有情况的答案,这种分布计数的思想是十分重要的。