壹、题目描述 ¶
贰、题解 ¶
个人认为官方题解说得很妙了,所以就去这里看吧......
只是推出来的式子有点不一样,我的是下面这样的:
\[2\sum_{p=1}^{m-1}\sum_{i=1}^n\sum_{j=i+1}^n{p-1+n-i\choose p-1}{p+i-1\choose i-1}{m+1-p-1+n-j\choose n-j}{m+1-p-2+j-1\choose j-1} \\ =2\sum_{p=1}^{m-1}\sum_{i=1}^n{p-1+n-i\choose p-1}{p+i-1\choose i-1}\sum_{j=i+1}^n{m-p+n-j\choose n-j}{m-p-2+j\choose j-1} \]注意到 \(i\) 上面的不能和 \(j\) 贴在一起,所以不应该是从 \((0,n)\) 走到 \((i,j)\),而是从 \((0,n)\) 走到 \((i-1,j)\).
对于 \(j\) 下面也是同理,应该走到 \((i+1+1,j)\) 而不是 \((i+1,j)\).
单独看一看后面的东西:
\[\sum_{j=i+1}^n{m+1-p-1+n-j\choose n-j}{m+1-p-2+j-1\choose j-1} \\ =\sum_{j=i+1}^n{m-p+n-j\choose n-j}{m-p+j-2\choose j-1} \]如果
\[f_{p,i}\overset{\Delta}{=}\sum_{j=1}^i{m+n-p-j\choose n-j}{m-p-2+j\choose j-1} \]显然有递推式
\[f_{p,i}=f_{p,i-1}+{m+n-p-i\choose n-i}{m-p-2+i\choose i-1} \]特别地,\(f_{p,0}=0\).
那么,答案计算就是
\[2\sum_{p=1}^{m-1}\sum_{i=1}^n{p-1+n-i\choose p-1}{p+i-1\choose i-1}(f_{p,n}-f_{p,i}) \]考虑蓝色无路径时,我们把棋盘翻转一下,是不是又变成了填黄色的状态了?
但是如果我们考虑黄色、蓝色无路径时都这么做,会将“黄蓝都没有路径”的方案算两次,所以我们随便找“考虑黄色”或者“考虑蓝色”的计算过程,将 \(j\in[i+1,n]\) 变成 \(j\in[i+2,n]\) 即可,反映到前缀和上就是 \(f_{p,n}-f_{p,i}\) 变成了 \(f_{p,n}-f_{p,i+1}\).
时间复杂度 \(\mathcal O(nm)\).
叁、参考代码 ¶
#include<cstdio>
#include<vector>
#include<cstring>
#include<algorithm>
using namespace std;
#define Endl putchar('\n')
#define mp(a, b) make_pair(a, b)
#define rep(i, l, r) for(int i=(l), i##_end_=(r); i<=i##_end_; ++i)
#define fep(i, l, r) for(int i=(l), i##_end_=(r); i>=i##_end_; --i)
#define fi first
#define se second
typedef long long ll;
typedef pair<int, int> pii;
template<class T>inline T fab(T x){ return x<0? -x: x; }
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;
}
const int maxn=2021*2;
const int mod=998244353;
int C[maxn+5][maxn+5];
int f[maxn+5][maxn+5];
int n, m;
inline void input(){
n=readin(1), m=readin(1);
for(int i=0; i<=maxn; ++i){
C[i][0]=C[i][i]=1;
for(int j=1; j<i; ++j)
C[i][j]=(C[i-1][j-1]+C[i-1][j])%mod;
}
}
inline void init(){
rep(p, 1, m){ f[p][0]=0;
rep(i, 1, n)
f[p][i]=(f[p][i-1]+1ll*C[m+n-p-i][n-i]*C[m-p+i-2][i-1]%mod)%mod;
}
}
signed main(){
input();
int ans=0;
// rep(p, 1, m-1) rep(i, 1, n) rep(j, i+1, n)
// ans=(ans+2ll*C[p-1+n-i][p-1]*C[p+i-1][i-1]%mod*C[m+1-p-1+n-j][n-j]%mod*C[m+1-p-2+j-1][j-1]%mod)%mod;
// swap(n, m);
// rep(p, 1, m-1) rep(i, 1, n) rep(j, i+2, n) // pay attention to +2
// ans=(ans+2ll*C[p-1+n-i][p-1]*C[p+i-1][i-1]%mod*C[m+1-p-1+n-j][n-j]%mod*C[m+1-p-2+j-1][j-1]%mod)%mod;
init();
rep(p, 1, m-1) rep(i, 1, n-1) ans=(ans+2ll*C[p-1+n-i][p-1]*C[p+i-1][i-1]%mod*(f[p][n]+mod-f[p][i+1])%mod)%mod;
swap(n, m); init();
rep(p, 1, m-1) rep(i, 1, n) ans=(ans+2ll*C[p-1+n-i][p-1]*C[p+i-1][i-1]%mod*(f[p][n]+mod-f[p][i])%mod)%mod;
printf("%d\n", ans);
return 0;
}
肆、用到 の Trick
分析方案的特殊性质,通过这种特殊性质寻找方案。
比如此处的特殊性在于:
both of the following cannot hold:
- There exists a path of yellow cells from column \(1\) to column \(m\).
- There exists a path of blue cells from row \(1\) to row \(n\).
这两种只要其中一个不成立即可。
另外,我们发现这个颜色是越来越短的,这里还用到了一个巧妙地做法即从 \((1,0)\) 之类的边缘点开始走,就可以很好地表示最左边的动态的最长长度,而不用花费其他的东西,而且这个做法本身也很妙,用组合数表示了黄色的“越来越短”。