Fun fact: For all AGC problems with score>=1600 this year, the number of accepted solutions is no more than 1. --Retired_MiFaFaOvO
A. Three Cells per Row and Column
- 将 \(n \times n\) 的方格黑白染色,使其满足:
- 每行每列均恰有 \(3\) 个黑格。
- 整个图中恰有 \(n\) 个黑色四联通块。
- \(6 \leq n \leq 500\)。
最简单构造题?
不考虑第二个限制,可以这样染:
#.....##
##.....#
###.....
.###....
...###..
....###.
.....###
对于第二个限制,考虑换一下行的顺序。
可以想到让每一行都形成一个联通块(前两行共同组成 \(2\) 个联通块),所以就是:
#.....##
##.....#
..###...
.....###
.###....
....###.
###.....
...###..
\(n=6\) 或 \(n=7\) 时会有点小问题,实测把第 \(2\) 行和第 \(4\) 行换一下即可。
代码里是按列写的。
#include<bits/stdc++.h>
using namespace std;
const int N=550;
int n,ans[N][N];
int main(){
cin>>n;
ans[1][1]=ans[1][2]=ans[2][2]=1;
ans[n][1]=ans[n][2]=ans[n-1][1]=1;
int nw=2;
for(int st=3;st>=1;st--){
for(int x=st;x+2<=n;x+=3){
nw++;
ans[x][nw]=ans[x+1][nw]=ans[x+2][nw]=1;
}
}
if(n==6 || n==7){
for(int i=1;i<=n;i++) swap(ans[i][2],ans[i][4]);
}
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
if(ans[i][j]) putchar('#');
else putchar('.');
}
putchar('\n');
}
}
B. Range Argmax
- 给定 \(M\) 个区间 \(\{(L_i,R_i)\}\)。求长度为 \(M\) 的数组 \(x_1,x_2,\cdots,x_m\) 的数量,满足:
- 存在长度为 \(N\) 的排列 \(p_1,\cdots,p_N\),满足 \(\forall i, p_{x_i}=\max\limits_{j=L_i}^{R_i} \{p_j\}\)。
- 答案对 \(998244353\) 取模。
- \(2 \leq N \leq 300\),\(1 \leq M \leq N(N-1)/2\),\(1 \leq L_i < R_i \leq N\),所有区间互不相同。
场上想了两小时没整出来
考虑直接数 \(x\),要求等价于若 \(i\) 和 \(j\) 区间分别选了 \(a\) 和 \(b\) 作为最大值且 \(a,b \in [L_i,R_i] \wedge [L_j,R_j]\),那么 \(a=b\)。然后就不会了
转换思路,考虑数 \(p\),但是为了避免算重我们让一个 \(x\) 只对应一个 \(p\)。
考虑怎样由 \(x\) 构造一个 \(p\),可以从大到小插入每个值,将其插入能插入的 最左端 的位置。
这样构造并没有什么性质能够快速判断 \(x\) 能否生成 \(p\)。但是我们发现每次插入一个值后,跨过该位置的区间的最大值已经被定下来了,而左右区间是独立的,可以考虑区间 DP。
对于区间 \([l,r]\),枚举它的最大值位置为 \(k\),即将它分成了 \([l,k)\) 和 \((k,r]\) 两部分。然而,为了确保不算重,我们需要保证 \(k\) 不能放在更左边的位置:
- 对于右边的区间,没有限制
- 对于左边的区间,若左边的区间选择了 \(t\),那么必须有一个区间同时包含 \(t\) 与 \(k\),否则 \(k\) 就可以放在 \(t\) 而不改变了 \(x\) 数组了。
于是设 \(f_{l,r,k}\) 表示区间 \([l,r]\) 的最大值位置为 \(k\) 时的答案,可以枚举左区间最大值位置 \(t\) 转移,时间复杂度 \(O(n^4)\)。
至于判断是否有区间同时包含 \(t\) 与 \(k\),也可以用一些高超的写法做到 \(O(n^3)\)。
使用前缀和优化,总时间复杂度 \(O(n^3)\)。
所以你管这叫 B(900) ???
#include<bits/stdc++.h>
using namespace std;
const int N=330,mod=998244353;
int n,m,ans;
int G[N][N],sG[N][N],pre[N][N],f[N][N][N],sf[N][N][N];
int main(){
cin>>n>>m;
for(int i=1;i<=m;i++){
int l,r;
cin>>l>>r;
G[l][r]=1;
}
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
sG[i][j]=sG[i][j-1]+G[i][j];
for(int len=1;len<=n;len++){
for(int l=1;l+len-1<=n;l++){
int r=l+len-1,p=l;
for(int k=l;k<=r;k++){
int tot=(k==r)+(sf[k+1][r][r]+mod-sf[k+1][r][k])%mod;
while(p<=r && sG[p][r]-sG[p][k-1]==0) p++;
if(p<k) f[l][r][k]=(f[l][r][k]+(sf[l][k-1][k-1]+mod-sf[l][k-1][p-1])%mod)%mod;
if(k==l) f[l][r][k]++;
f[l][r][k]=1ll*f[l][r][k]*tot%mod;
sf[l][r][k]=(sf[l][r][k-1]+f[l][r][k])%mod;
}
}
}
cout<<sf[1][n][n];
}
C. 01 Balanced
- 给出 \(M\) 个区间 \(\{[L_i,R_i]\}\),求字典序最小的长度为 \(N\) 的 01 串 \(s\),满足给出的所有区间内 \(0\) 与 \(1\) 的数量相等。
- \(2 \leq N \leq 10^6\),\(1 \leq M \leq 2 \times 10^5\),\(1 \leq L_i < R_i \leq N\),\(2 \mid R_i-L_i+1\),所有区间互不相同。
因为不会差分约束而没有切掉
考虑构造一种前缀和 \(S_i=S_{i-1}+(-1)^{s_i}\)。则题目限制等价于 \(S_{L_i-1}=S_{R,i}\)。
另一方面,限制是 \(|S_i-S_{i-1}|=1,i \in [1,N]\)。
考虑差分约束,将第二个限制视为 \(|S_i-S_{i-1}| \leq 1\),从 \(0\) 开始跑最短路。
由于区间长度为偶数且跑的是最短路,不可能出现 \(S_i=S_{i-1}\) 的情况。
由于边权只有 \(0\) 和 \(1\),可以跑 Dijkstra,甚至 BFS 都行,时间复杂度 \(O(N+M)\)。
#include<bits/stdc++.h>
using namespace std;
const int N=2e6+5,M=2e6+5;
int n,m,s;
struct nod{
int to,nxt,w;
}e[M*2];
int head[N],cnt;
void add(int u,int v,int w){
e[++cnt].to=v;
e[cnt].w=w;
e[cnt].nxt=head[u];
head[u]=cnt;
}
int dis[N];
bool vis[N];
struct abc{
int num,dis;
};
bool operator <(abc x,abc y){
return x.dis>y.dis;
}
priority_queue <abc> q;
int main(){
cin>>n>>m;
for(int i=1;i<=n;i++){
add(i-1+1,i+1,1);
add(i+1,i-1+1,1);
}
for(int i=1;i<=m;i++){
int u,v;
cin>>u>>v;
u--;
add(u+1,v+1,0);
add(v+1,u+1,0);
}
memset(dis,0x7f,sizeof(dis));
dis[1]=0;
q.push((abc){1,0});
while(!q.empty()){
abc tmp=q.top();
q.pop();
int u=tmp.num;
if(vis[u]) continue;
vis[u]=1;
for(int i=head[u];i;i=e[i].nxt){
int v=e[i].to;
if(!vis[v] && dis[u]+e[i].w<dis[v])
dis[v]=dis[u]+e[i].w,q.push((abc){v,dis[v]});
}
}
for(int i=2;i<=n+1;i++){
if(dis[i]>dis[i-1]) putchar('0');
else putchar('1');
}
}
D. Subset Sum Game
- 黑板上有 \(N\) 个数字 \(A_1,\cdots,A_N\) 和两个数 \(L,R\)。
- Alice 和 Bob 在玩游戏,Alice 先手。每次操作是擦去黑板上的一个数字。
- \(\frac{N}{2}\) 轮后,若 Alice 擦去的数之和 \(s\) 满足 \(L \leq s \leq R\),Alice 获胜;否则 Bob 获胜。
- 若两人均采取最优策略,问最后谁会获胜。
- \(2 \leq N \leq 5000\),\(2 \mid N\),\(1 \leq A_i \leq 10^9\),\(0 \leq L \leq R \leq \sum A_i\)。
还不是很懂结论和证明……
先考虑转化条件。设 \(S=\sum A_i\),\(sA,sB\) 分别代表两人所取走的数之和。
则 \(sA-sB=sA-(S-sA)=2sA-S\),\(sA=\dfrac{1}{2}(sA-sB+S)\)。
\(L \leq sA \leq R\) 等价于 \(L \leq \dfrac{1}{2}(sA-sB+S) \leq R\),即 \(2L-S \leq sA-sB \leq 2R-S\)。
再继续简化,设 \(X=L+R-S\),则上等价于 \(|(sA-sB)-X| \leq R-L\)。
问题等价于:
有一个变量 \(x\) 初始为 \(L+R-S\),Alice 可以选择一个 \(A_i\) 给 \(x\) 加上,而 Bob 可以选择一个 \(A_i\) 给 \(x\) 减掉。Alice 想最大化 \(|X|\) 而 Bob 相反,求 \(x\) 最后的值。
有结论:
对于所有 \(p\),将 \(p,p+X,A_1,\cdots,A_N\) 从小到大排序为 \(a_1,\cdots,a_{N+2}\),则 \(x\) 最后的值即为 \((a_2-a_1)+(a_4-a_3)+\cdots+(a_{N+2}-a_{N+1})\) 的最小值。
暴力枚举 \(p\) 显然是不行的,但是若 \(p\) 和 \(p+X\) 均落在两个区间中间显然可以左右移动变得更小,所以只需考虑 \(p\) 或 \(p+X\) 为 \(A_t\) 中的某个即可。
时间复杂度 \(O(N^2 \log N)\)。
#include<bits/stdc++.h>
using namespace std;
const long long N=5500,INF=1e18;
long long n,L,R,S,X,anss=INF;
long long a[N];
long long b[N],id;
void solve(long long x){
id=0;
if(x<a[1]) b[++id]=x;
if(x+X<a[1]) b[++id]=x+X;
for(long long i=1;i<=n;i++){
b[++id]=a[i];
if(a[i]<=x && x<a[i+1]) b[++id]=x;
if(a[i]<=x+X && x+X<a[i+1]) b[++id]=x+X;
}
for(int i=1;i<id;i++) if(b[i]>b[i+1]) swap(b[i],b[i+1]);
long long ans=0;
for(long long i=1;i<=id;i+=2) ans+=b[i+1]-b[i];
anss=min(anss,ans);
}
int main(){
cin>>n>>L>>R;
for(long long i=1;i<=n;i++) cin>>a[i],S+=a[i];
sort(a+1,a+n+1);
a[n+1]=INF;
X=S-(L+R);
for(long long i=1;i<=n;i++) solve(a[i]),solve(a[i]-X);
if(anss<=R-L) puts("Alice");
else puts("Bob");
}
E. Cheese
- 有一个长度为 \(N\) 的圆周,设某点 \(O\) 位置为 \(0\),圆周上一点的位置定义为从 \(O\) 沿顺时针到该点的距离。有 \(N\) 只老鼠分别站在 \(0.5,1.5,\cdots,N-0.5\) 的位置,有 \(N\) 个放奶酪的位置 \(0,1,\cdots,N-1\),位置 \(i\) 放奶酪的概率为 \(A_i\)。
- 每一次操作,按 \(A_i\) 概率随机在某个位置放上一块奶酪。这块奶酪随后会沿着圆周顺时针游走,直到它被吃掉;当它遇到一只未吃过奶酪的老鼠时,有一半的概率被吃掉。
- 对每只老鼠求出 \(N-1\) 次操作后,它还未吃到奶酪的概率,对 \(998244353\) 取模。
- \(1 \leq N \leq 40\)。
赛后直接切了,你管这叫 E(1600)?
发现放奶酪的顺序似乎无关紧要。如果有一前一后两个奶酪,我们可以不管顺序,让后面的奶酪先走到前面的奶酪处,再两块奶酪一起走。这样显然不影响结果(因为奶酪和老鼠间不区分)。
考虑计算最后老鼠 \(X\) 还未吃到奶酪的概率。
参考上面的思路,我们可以先把所有奶酪放好(最后要乘上可重排),再让所有奶酪转到 \(X\) 前面(这个过程中不会有奶酪经过 \(X\)),这时候只要知道还剩下多少奶酪(或老鼠)即可快速算出答案。
先考虑第一步。设 \(f_{i,j,k}\) 表示转移到 \(i+X\) 位置,已经放了 \(j\) 块奶酪,老鼠们已经吃掉放上去的 \(k\) 块奶酪的概率,最后 \(f_{N,N-1,k}\) 即是需要的东西。
转移需要分两步(放奶酪,奶酪走一步让老鼠吃)。
放奶酪(注意可重排):
\[f_{i,j+p,k} \leftarrow f_{i,j+p,k}+\dfrac{1}{p!} A_{i+k}^p f_{i,j,k} \]吃奶酪:
\[f_{i+1,j,k} \leftarrow f_{i+1,j,k}+\dfrac{1}{2^{j-k}} f_{i,j,k} \] \[f_{i+1,j,k+1} \leftarrow f_{i+1,j,k+1}+(1-\dfrac{1}{2^{j-k}}) f_{i,j,k} \]单次时间复杂度 \(O(N^4)\)。
再考虑第二步。假设现在还剩下 \(k-1\) 块奶酪(都在 \(X\) 前面)和 \(k\) 只老鼠,我们要求最后 \(X\) 吃不到奶酪的概率。
打表发现答案是 \(\dfrac{1}{2^{k}-1}\),为什么呢?
将老鼠按顺时针编号,\(X\) 编号为 \(0\)。设 \(p_i\) 表示编号为 \(i\) 的老鼠最后吃不到奶酪的概率。
只考虑老鼠 \(i\) 与老鼠 \(i+1\)(\(i \in [0,k)\)),假设现在轮到 \(i\),还剩下 \(x\) 块奶酪,有 \(4\) 种情况:
- \(i\) 和 \(i+1\) 都没吃,区分不出来,不考虑(留到下一轮考虑)。
- \(i\) 和 \(i+1\) 都吃了,区分不出来,不考虑。
- \(i\) 吃了但 \(i+1\) 没吃,概率为 \((1-\dfrac{1}{2^x})\dfrac{1}{2^{x-1}}\)。
- \(i\) 没吃但 \(i+1\) 吃了,概率为 \((1-\dfrac{1}{2^{x-1}})\dfrac{1}{2^x}\)。
所以有 \(p_{i+1}:p_i=(1-\dfrac{1}{2^x})\dfrac{1}{2^{x-1}}:(1-\dfrac{1}{2^{x-1}})\dfrac{1}{2^x}=2:1\),即 \(p_{i+1}=2p_i\)。
自然可得 \(p_i=2^i p_i\)(\(i \in [0,k)\))。
又有 \(\sum p_i=1\),于是 \((2^k-1)p_0=1\),得 \(p_0=\dfrac{1}{2^k-1}\)。得证。
总时间复杂度 \(O(n^5)\),超好写。
#include<bits/stdc++.h>
using namespace std;
const long long N=44,mod=998244353,inv2=(mod+1)/2;
long long mul[N],inv[N],pw[N],ipw[N];
long long ksm(long long a,long long x){
long long tot=1;
while(x){
if(x & 1ll) tot=1ll*tot*a%mod;
a=1ll*a*a%mod;
x>>=1ll;
}
return tot;
}
long long n,A[N],ans;
long long f[2][N][N],id;
int main(){
cin>>n;
for(long long i=0;i<n;i++) cin>>A[i],A[i]=1ll*A[i]*ksm(100,mod-2)%mod;
mul[0]=inv[0]=1;
for(long long i=1;i<=n;i++) mul[i]=mul[i-1]*i%mod;
inv[n]=ksm(mul[n],mod-2);
for(long long i=n-1;i>=1;i--) inv[i]=inv[i+1]*(i+1)%mod;
pw[0]=ipw[0]=1;
for(long long i=1;i<=n;i++) pw[i]=pw[i-1]*2%mod,ipw[i]=ipw[i-1]*inv2%mod;
for(long long X=0;X<n;X++){
memset(f,0,sizeof(f));
id=0;
f[0][0][0]=1;
for(long long i=1;i<=n;i++){
id^=1;
memset(f[id],0,sizeof(f[id]));
for(long long j=0;j<=n;j++){
for(long long k=0;k<=n;k++){
for(long long p=0;j+p<=n;p++)
f[id][j+p][k]=(f[id][j+p][k]+inv[p]*ksm(A[(X+i)%n],p)%mod*f[id^1][j][k]%mod)%mod;
}
}
if(i==n) break;
id^=1;
memset(f[id],0,sizeof(f[id]));
for(long long j=0;j<=n;j++){
for(long long k=0;k<=j;k++){
f[id][j][k]=(f[id][j][k]+ipw[j-k]*f[id^1][j][k]%mod)%mod;
f[id][j][k+1]=(f[id][j][k+1]+(1+mod-ipw[j-k])*f[id^1][j][k]%mod)%mod;
}
}
}
ans=0;
for(long long i=0;i<=n;i++){
ans=(ans+mul[n-1]*f[id][n-1][i]%mod*ksm(pw[n-i]-1,mod-2)%mod)%mod;
}
cout<<ans<<" ";
}
}
F. Degree Sequence in DFS Order
- 求所有长度为 \(N\) 的序列 \(a_1,\cdots,a_N\),满足:
- 存在一张 \(N\) 个点 \(M\) 条边无自环(可重边)的无标号无向图,设其 DFS 序(点序列)之一为 \(d_1,\cdots,d_n\),满足 \(a_i\) 为 \(d_i\) 的度数。
- 答案对 \(998244353\) 取模。
- \(2 \leq N \leq M \leq 10^6\)。
这比上场 F 阳间多了
(写完后)阳间个鬼,这官方题解真不是一般的简略,证明根本看不懂啊啊啊啊
Part 1
不妨按 DFS 序标号,即存在 DFS 序之一为 \(1,2,\cdots,N\)。(\(*\))
显然必要条件是:对于所有 \(u \in [2,N]\),存在 \(v<u\) 使 \((u,v)\) 间连边。即若现在已走过 \(1 \sim u\),可以走到 \(u+1\)。(\(**\))
但这并不是充分条件,因为 DFS 序会走到底,可能无法选择 \(u+1\)。
注意到若有 \(a<b<c<d\) 满足 \((a,c)\) 和 \((b,d)\) 连边,可以改为 \((a,d)\) 和 \((b,c)\) 连边,这样度数与性质均不变。
令边 \((u,v)\) 的边权为 \((v-u)^2\),则每次操作必然增大边权和,故存在边权和最大的终态无法继续操作。
假设已走过 \(1 \sim u\),\(u\) 节点不与 \(u+1\) 相连。设 \(u+1\) 与 \(v\) 相连,此时 \(1 < v < u < u+1\) 而 \((1,u)\) 与 \((v,u+1)\) 间连边,矛盾。因此这个终态是满足(\(*\))的。
这样,每一个满足(\(**\))的图都可生成一个满足(\(*\))的图。我们只需要数满足(\(**\))的图的度数序列数量即可。
Part 2
我们来考虑 \(a\) 的限制。
首先显然有 \(\sum\limits_{i=1}^N a_i=2M\)。
更细节的,由于每个节点都向前面连边,可以整体考虑前 \(u\) 个点:
- \(\sum\limits_{i \leq u} a_i \geq 2(u-1)+1\)(\(+1\) 是因为前 \(u\) 个点中至少一个要向 \(u+1\) 连边)。(条件 \(1\))
- 同时还有下限 \(a_{u+1} \leq M-(u-1)\)。(条件 \(2\))
这是必要条件,令人惊讶的是同时也是充分条件。证明如下:
考虑一轮贪心:对于每个点 \(2,3,\cdots,N\),从前面的点选出一个剩余度数最大的点连边。(由于条件 \(1\),可以做到)
如果一轮后所有点剩余度数均不超过 \(M-(N-1)\),就可以贪心匹配。
假设有 \(a_i'>M-(N-1)\),那么由于 \(\sum a_t'=2M-2(N-1)\),不存在另外的 \(a_j' \geq M-(N-1)\)。由条件 \(2\),\(a_i \leq M-(i-2)\)。下面说明在贪心过程中,\(i\) 号点连接了 \(N-i+1\) 条边(也是至多),则有 \(a_i' \leq M-(i-2)-(N-i+1)=M-(N-1)\),矛盾。
首先,在贪心到 \(i\) 时,\(i\) 一定会向前面连一条边。然后,在贪心到 \(k>i\) 时,若存在剩余度数 \(a_{(now)j}>a_{(now)i}\),由于 \(a_i'\) 顶多减到 \(M-(N-1)-1\),\(a_j' \geq M-(N-1)\),这与假设矛盾。因此每一次都可以选择 \(a_i\),\(a_i\) 连的总度数即为 \(N-i+1\)。证毕。
Part 3
现在真的要开始计数了!
先做些变换。令 \(b_0=a_1\),\(b_i=a_{i+1}-1,i \in [1,N)\)(注意所有 \(b_i\) 均为非负整数),则三个条件变为:
- \[\sum\limits_{i=0}^{n-1} b_i=2M-(N-1) \]
- \[\sum\limits_{i < u} b_i \geq u \]
- \[b_u \leq M-u \]
这是一个类似卡特兰数的东西,考虑对应到图上。
如果不考虑第三个条件,相当于从原点出发,每次先向上走 \(b_0\),再右移一格,再向上走 \(b_1\),再右移一格……向上走 \(b_{N-1}\)。最后走到 \((N-1,2M-(N-1))\)。限制是不能越过 \(y=x\) 这条线。
用卡特兰数的算法可以轻松算出是 \(\dbinom{2M}{N-1}-\dbinom{2M}{N-2}\)。
考虑减去不满足第三个条件的情况。这里又有一个性质:至多只存在一个 \(K\) 使 \(b_K > M-K\)。证明如下:
设 \(p<q\) 使 \(b_p > M-p\),\(b_q > M-q\)。由条件 \(2\),\(\sum\limits_{i<p} b_i \geq p\),所以 \(\sum\limits_{i=0}^{N-1} b_i \geq \sum\limits_{i<p} b_i+b_p+b_q \geq p+(M-p+1)+(M-q+1)=2M-(q-2) > 2M-(N-1)\),矛盾。
所以,加上第三个条件。我们钦定了 \(b_K \geq M-K+1\),为了维护 \(b_i\) 取非负整数,令 \(b_K \leftarrow b_K-(M-K+1)\),则 \(\sum\limits_{i=0}^{N-1} b_i=2M-(N-1)-(M-K+1)=M-N+K\)。现在的图看起来是这样的:
(图来自官方题解)
不妨将图分为三角形与矩形上下两部分来考虑。具体而言,考虑枚举 \(x\),而第一次跃过 \(K\) 的点 \((x,y)\)(即 \(y \geq K\)),分成前后两部分计数。方案数实际等于 \((0,0) \rightarrow (x,K-1)\) 乘上 \((x,K) \rightarrow (N-1,M-N+K)\)(想一想,为什么?)。
于是答案为(\(K=0\) 时与 \(K=1\) 答案相同,但是要特判):
\[Ans=\dbinom{2M}{N-1}-\dbinom{2M}{N-2}-\dbinom{M-1}{M-N}+\sum\limits_{k=1}^{N-1} \sum\limits_{x=0}^{k-1} \Big [\dbinom{x+k-1}{x}-\dbinom{x+k-1}{x-1} \Big] \dbinom{M-x-1}{M-N} \]交换和号~
\[Ans=\dbinom{2M}{N-1}-\dbinom{2M}{N-2}-\dbinom{M-1}{M-N}+\sum\limits_{x=0}^{N-1} \dbinom{M-x-1}{M-N} \sum\limits_{k=x+1}^{N-1} \dbinom{x+k-1}{x}-\dbinom{x+k-1}{x-1} \]众所周知 \(\sum\limits_{i=0}^n \dbinom{i}{m}=\dbinom{n+1}{m+1}\),所以拆开得到:
\[Ans=\dbinom{2M}{N-1}-\dbinom{2M}{N-2}-\dbinom{M-1}{M-N}+\sum\limits_{x=0}^{N-1} \dbinom{M-x-1}{M-N} \Big[ \dbinom{x+n-1}{x+1}-\dbinom{2x-1}{x+1}-\dbinom{x+n-1}{x}+\dbinom{2x-1}{x}+[x=0]\Big ] \]预处理组合数,时间复杂度 \(O(N+M)\)。
这短短的三行代码后是多少艰辛的尝试与推导啊……
#include<bits/stdc++.h>
using namespace std;
const long long N=2e6+5,mod=998244353;
long long mul[N],inv[N];
long long ksm(long long a,long long x){
long long tot=1;
while(x){
if(x & 1ll) tot=1ll*tot*a%mod;
a=1ll*a*a%mod;
x>>=1ll;
}
return tot;
}
void init(long long lim){
mul[0]=inv[0]=1;
for(long long i=1;i<=lim;i++) mul[i]=mul[i-1]*i%mod;
inv[lim]=ksm(mul[lim],mod-2);
for(long long i=lim-1;i>=1;i--) inv[i]=inv[i+1]*(i+1)%mod;
}
long long C(long long m,long long n){
if(m<n || m<0 || n<0) return 0;
return mul[m]*inv[n]%mod*inv[m-n]%mod;
}
long long n,m,ans;
int main(){
init(N-5);
cin>>n>>m;
for(long long x=0;x<n;x++)
ans=(ans+mod-(C(x+n-1,x+1)+mod-C(2*x-1,x+1)+mod-C(x+n-1,x)+C(2*x-1,x)+mod+(!x))%mod*C(m-x-1,m-n)%mod)%mod;
cout<<(ans+C(2*m,n-1)+mod-C(2*m,n-2)+mod-C(m-1,m-n))%mod;
}