2025牛客多校第六场 D.漂亮矩阵 K.最大gcd C.栈 L.最小括号串 个人题解

发布于:2025-08-04 ⋅ 阅读:(9) ⋅ 点赞:(0)

L.最小括号串

#数组操作 #贪心

题目

![[Pasted image 20250802151852.png]]

思路

感谢Leratiomyces大佬赛时的提示,否则估计还一直签不了到()

首先,贪心地构造出最优情况:数组左半部分全是(,右半部分全是),随后通过判断给定的区间中是否包含(来决定是否调整当前序列。

对给定的区间按照左端点降序排序,这样就可以从右往左有序遍历所有区间。

设置两个指针 i d l , i d r id_{l},id_{r} idl,idr

  • i d l id_{l} idl指向未被调换的(中最右的(的下标
  • i d r id_{r} idr指向经过调换后的(中最左的(的下标

从右往左有序遍历所有区间:

  • 如果当前区间右端点 r r r i d r id_{r} idr左侧,那么说明当前区间 [ l , r ] [l,r] [l,r]中一定没有(,因此需要调度一个(前往 l l l位置(尽量保证字典序小),即 s w a p ( a n s [   i d l   ] , a n s [   l   ] ) swap(ans[\ id_{l}\ ],ans[\ l\ ]) swap(ans[ idl ],ans[ l ])
  • 调度前需要注意 i d l id_{l} idl是否为0,即序列左端是否还剩余(。如果 i d l = = 0 id_{l}==0 idl==0而仍然需要调度(,那么必然是不合法的,输出-1

最后不要忘了遍历整个序列判断是否会存在)(的非法情况

代码实现

#include<iostream>
#include<vector>
#include<algorithm>
#include<map>
#include<unordered_set>
#include<queue>
using namespace std;
using ll = long long;
#define rep(i, a, b) for(ll i = (a); i <= (b); i++)
#define per(i, a, b) for(ll i = (a); i >= (b); i--)
#define mid ((l+r)>>1)
#define see(stl) for(auto&ele:stl)cout<<ele<<" "; cout<<'\n';
const int N=1e5+5;
int n,m;
struct lr{
    int l,r;
    bool operator<(const lr&t)const{
        return l>t.l;
    }
};
void solve() {
    cin>>n>>m;
    n*=2;
    vector<lr>b(m+1);    
    vector<char>ans(n+1);
    rep(i,1,m){
        int l,r;cin>>l>>r;
        b[i]={l,r};
    }
    rep(i,1,n){
        if(i<=n/2)ans[i]='(';
        else ans[i]=')';
    }
    sort(b.begin()+1,b.begin()+1+m);
    int idl=n/2,idr=n+1;
    rep(i,1,m){
        int l=b[i].l,r=b[i].r;
        if(r<idr){
            if(idl<1){
                cout<<-1<<'\n';return;
            }
            swap(ans[idl],ans[l]);
            idr=l;
            idl--;
        }
    }
    int cntl=0,cntr=0;
    rep(i,1,n){
        if(ans[i]=='(')cntl++;
        else cntr++;
        if(cntr>cntl){
            cout<<-1<<'\n';return;
        }
    }
    rep(i,1,n)cout<<ans[i];
    cout<<'\n';
}

int main() {
    ios::sync_with_stdio(0);
    cin.tie(0), cout.tie(0);
    int t = 1;
    cin >> t;
    while (t--) {
        solve();
    }
    return 0;
}

C.

#数学 #组合数 #斯特林数

题目

![[Pasted image 20250802154220.png]]

思路

提供一种纯数学的方法:

S n m S_{n}^m Snm为长度为 n n n的所有排列中 s i z e size size m m m的数量
易知 ∑ i = 1 n S n i = n ! \sum_{i=1}^nS_{n}^i=n! i=1nSni=n!,即排列数 A n n A_{n}^n Ann

接下来可以找出他的递推:

  • 当数字 n n n不位于排列的最后一位时, s t a c k stack stack p o p pop pop功能必定会将其 p o p pop pop掉,即数字 n n n不产生贡献。数字 n n n不位于最后一位的可能有 n − 1 n-1 n1种,那么就可以将此时的情况等价于 长度为 n − 1 的所有排列中 s i z e 为 m 的数量 长度为n-1的所有排列中size为m的数量 长度为n1的所有排列中sizem的数量,即 ( n − 1 ) × S n − 1 m (n-1)\times S_{n-1}^m (n1)×Sn1m
  • 当数字 n n n位于排列的最后一位时,其必然对 s i z e size size产生 1 1 1的贡献,那么就可以将此时的情况等价于 长度为 n − 1 的所有排列中 s i z e 为 m − 1 的数量 长度为n-1的所有排列中size为m-1的数量 长度为n1的所有排列中sizem1的数量,即 S n − 1 m − 1 S_{n-1}^{m-1} Sn1m1
    综上:
    S n m = ( n − 1 ) × S n − 1 m + S n − 1 m − 1 S_{n}^m=(n-1)\times S_{n-1}^m+S_{n-1}^{m-1} Snm=(n1)×Sn1m+Sn1m1
    则答案所求即:
    ∑ i = 1 n i 3 ⋅ S n i \sum_{i=1}^n i^3·S_{n}^i i=1ni3Sni

为了求解答案,我们设 s u m j [ n ] = ∑ i = 1 n i j ⋅ S n i sum_{j}[n]=\sum_{i=1}^ni^j·S_{n}^i sumj[n]=i=1nijSni,其中:

  • s u m 0 [ n ] = ∑ i = 1 n S n i = n ! sum_{0}[n]=\sum_{i=1}^nS_{n^i}=n! sum0[n]=i=1nSni=n!
  • 所求答案即 s u m 3 [ n ] sum_{3}[n] sum3[n]

接下来我们来研究 s u m j sum_{j} sumj之间的递推:
s u m 1 [ n ] = ∑ i = 1 n i ⋅ S n − 1 i 带入递推式 S n m = ( n − 1 ) × S n − 1 m + S n − 1 m − 1 : = ∑ i = 1 n i ⋅ [   ( n − 1 ) S n − 1 i + S n − 1 i − 1 ] = ( n − 1 ) ∑ i = 1 n i ⋅ S n − 1 i + ∑ i = 1 n i ⋅ S n − 1 i − 1 = ( n − 1 ) s u m 1 [ n − 1 ] + ∑ i = 0 n − 1 ( i + 1 ) ⋅ S n − 1 i = ( n − 1 ) s u m 1 [ n − 1 ] + ∑ i = 1 n − 1 i ⋅ S n − 1 i + ∑ i = 1 n − 1 S n − 1 i = ( n − 1 + 1 ) s u m 1 [ n − 1 ] + s u m 0 [ n − 1 ] s u m 1 [ n ] = n ⋅ s u m 1 [ n − 1 ] + s u m 0 [ n − 1 ] \begin{align} sum_{1}[n]&=\sum_{i=1}^ni·S_{n-1}^i\\ \\ &带入递推式S_{n}^m=(n-1)\times S_{n-1}^m+S_{n-1}^{m-1}:\\ \\ &=\sum_{i=1}^ni·[\ (n-1)S_{n-1}^i+S_{n-1}^{i-1}]\\ \\ &=(n-1)\sum_{i=1}^ni·S_{n-1}^i+\sum_{i=1}^ni·S_{n-1}^{i-1}\\ \\ &=(n-1)sum_{1}[n-1]+\sum_{i=0}^{n-1}(i+1)·S_{n-1}^i\\ \\ &=(n-1)sum_{1}[n-1]+\sum_{i=1}^{n-1}i·S_{n-1}^i+\sum_{i=1}^{n-1}S_{n-1}^i\\ \\ &=(n-1+1)sum_{1}[n-1]+sum_{0}[n-1]\\ \\ sum_{1}[n]&=n·sum_{1}[n-1]+sum_{0}[n-1] \end{align} sum1[n]sum1[n]=i=1niSn1i带入递推式Snm=(n1)×Sn1m+Sn1m1:=i=1ni[ (n1)Sn1i+Sn1i1]=(n1)i=1niSn1i+i=1niSn1i1=(n1)sum1[n1]+i=0n1(i+1)Sn1i=(n1)sum1[n1]+i=1n1iSn1i+i=1n1Sn1i=(n1+1)sum1[n1]+sum0[n1]=nsum1[n1]+sum0[n1]
因此我们可以完全类比推导过程得出 s u m j [ n ] sum_{j}[n] sumj[n]的公式:
s u m j [ n ] = ∑ i = 1 n i j ⋅ S n i = ∑ i = 1 n i j ⋅ [ ( n − 1 ) S n − 1 i + S n − 1 i − 1 ] = ( n − 1 ) s u m j [ n − 1 ] + ∑ i = 1 n i j ⋅ S n − 1 i − 1 = ( n − 1 ) s u m j [ n − 1 ] + ∑ i = 0 n − 1 ( i + 1 ) j ⋅ S n − 1 i = ( n − 1 ) s u m j [ n − 1 ] + ∑ i = 1 n − 1 ∑ k = 0 j C j k ⋅ i k ⋅ S n − 1 i = ( n − 1 ) s u m j [ n − 1 ] + ∑ k = 0 j C j k ∑ i = 1 n − 1 i k ⋅ S n − 1 i s u m j [ n ] = ( n − 1 ) s u m j [ n − 1 ] + ∑ k = 0 j s u m k [ n − 1 ] \begin{align} &sum_{j}[n]=\sum_{i=1}^ni^j·S_{n}^i\\ \\ &=\sum_{i=1}^ni^j·[(n-1)S_{n-1}^i+S_{n-1}^{i-1}]\\ \\ &=(n-1)sum_{j}[n-1]+\sum_{i=1}^ni^j·S_{n-1}^{i-1}\\ \\ &=(n-1)sum_{j}[n-1]+\sum_{i=0}^{n-1}(i+1)^j·S_{n-1}^i\\ \\ &=(n-1)sum_{j}[n-1]+\sum_{i=1}^{n-1}\sum_{k=0}^jC_{j}^k·i^k·S_{n-1}^i\\ \\ &=(n-1)sum_{j}[n-1]+\sum_{k=0}^jC_{j}^k\sum_{i=1}^{n-1}i^k·S_{n-1}^i\\ \\ sum_{j}[n]&=(n-1)sum_{j}[n-1]+\sum_{k=0}^jsum_{k}[n-1] \end{align} sumj[n]sumj[n]=i=1nijSni=i=1nij[(n1)Sn1i+Sn1i1]=(n1)sumj[n1]+i=1nijSn1i1=(n1)sumj[n1]+i=0n1(i+1)jSn1i=(n1)sumj[n1]+i=1n1k=0jCjkikSn1i=(n1)sumj[n1]+k=0jCjki=1n1ikSn1i=(n1)sumj[n1]+k=0jsumk[n1]
由此我们可以在 o ( n ) o(n) o(n)的复杂度下递推得到 s u m 3 [ n ] sum_{3}[n] sum3[n]

代码实现

#include<iostream>
#include<cstdio>
#include<vector>
#include<algorithm>
#include<queue>
#include<cmath>
#include<unordered_map>
#include<set>
using namespace std;
using ll = long long;
#define rep(i, a, b) for(ll i = (a); i <= (b); i ++)
#define per(i, a, b) for(ll i = (a); i >= (b); i --)
const int MOD = 998244353;
const int N = 5e5 + 5;

ll ans[N], sum3[N], sum2[N], sum1[N], sum0[N];

void precompute() {
    sum0[1] = 1;sum1[1] = 1;sum2[1] = 1;sum3[1] = 1;ans[1] = 1;  
    for (int n = 2; n < N; ++n) {
        sum0[n] = (n * sum0[n-1]) % MOD;
        sum1[n] = (n * sum1[n-1] + sum0[n-1]) % MOD;
        sum2[n] = (n * sum2[n-1] + 2 * sum1[n-1] + sum0[n-1]) % MOD;
        sum3[n] = (n * sum3[n-1] + 3 * sum2[n-1] + 3 * sum1[n-1] + sum0[n-1]) % MOD;       
        ans[n] = sum3[n];
    }
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(0);
    precompute();
    int T;
    cin >> T;
    while (T--) {
        int n;
        cin >> n;
        cout << ans[n] << '\n';
    }
    return 0;
}

K. 最大gcd

#数学 #gcd #差分

题目

![[Pasted image 20250802165206.png]]

思路

已知gcd具有特殊性质:
g c d ( a 1 , a 2 , … , a n ) = g c d ( a 1 , a 2 − a 1 , a 3 − a 2 , … , a n − a n − 1 ) gcd(a_{1},a_{2},\dots,a_{n})=gcd(a_{1},a_{2}-a_{1},a_{3}-a_{2},\dots,a_{n}-a_{n-1}) gcd(a1,a2,,an)=gcd(a1,a2a1,a3a2,,anan1)
设差分数组 b [ N ] , b [ i ] = a [ i ] − a [ i − 1 ] b[N],b[i]=a[i]-a[i-1] b[N],b[i]=a[i]a[i1]

则对于区间 [ l , r ] [l,r] [l,r]内每个数都加 k k k相当于对 b [ l ] + = k , b [ r + 1 ] − = k b[l]+=k,b[r+1]-=k b[l]+=k,b[r+1]=k

  • 首先讨论整个数组都 + k +k +k的情况:
    • 操作 b [ 1 ] + = k b[1]+=k b[1]+=k即可( b [ r + 1 ] b[r+1] b[r+1]不存在)
    • 此时的gcd为 g c d ( b 1 + k , b 2 , … , b n ) gcd(b_{1}+k,b_{2},\dots,b_{n}) gcd(b1+k,b2,,bn)
    • 必定存在 k k k使得 g c d ( b 2 , b 3 , … , b n ) ∣ ( b 1 + k ) gcd(b_{2},b_{3},\dots,b_{n})|(b_{1}+k) gcd(b2,b3,,bn)(b1+k)
    • 因此该情况的gcd记为 g 1 = g c d ( b 2 , b 3 , … , b n ) g_{1}=gcd(b_{2},b_{3},\dots,b_{n}) g1=gcd(b2,b3,,bn)
  • 接下来讨论局部 + k +k +k的情况:
    • 由于是局部操作,所以 b 1 , b n b_{1},b_{n} b1,bn这两个的其中一个必定不会被 + k +k +k,因此枚举 b 1 , b n b_{1},b_{n} b1,bn的所有因数 f f f
    • 若所有的 b i b_{i} bi都为 f f f的倍数,那么当前的 f f f一定是合法的gcd
    • 若只有一个 b i b_{i} bi不是 f f f的倍数,那么一定存在一个 k k k使得 f ∣ ( b i + k ) f|(b_{i}+k) f(bi+k),当前的 f f f也是合法的
    • 若超过两个 b i b_{i} bi不是 f f f的倍数,那么无论怎么操作都不可能使得整个数组的gcd为 f f f
    • 若只有两个 b i b_{i} bi不是 f f f的倍数,那么需要特殊判断。在此提供两种判断方法:
      • 方法一:
        • 设不是 f f f的两个数为 b 1 , b 2 b_{1},b_{2} b1,b2,则 b 1 = t 1 × f + b 1 % f , b 2 = t 2 × f + b 2 % f b_{1}=t_{1}\times f+b_{1}\%f,b_{2}=t_{2}\times f+b_{2}\%f b1=t1×f+b1%f,b2=t2×f+b2%f
        • k = b 1 % f k=b_{1}\%f k=b1%f,则 b 1 − k = t 1 × f b_{1}-k=t_{1}\times f b1k=t1×f f f f的倍数;
        • b 2 + k = t 2 × f + b 1 % f + b 2 % f b_{2}+k=t_{2}\times f+b_{1}\%f+b_{2}\%f b2+k=t2×f+b1%f+b2%f要为 f f f的倍数,则必有 ( b 1 % f + b 2 % f ) % f = 0 (b_{1}\%f+b_{2}\%f)\%f=0 (b1%f+b2%f)%f=0
        • 因此可以通过判别式 ( b 1 % f + b 2 % f ) % f = 0 (b_{1}\%f+b_{2}\%f)\%f=0 (b1%f+b2%f)%f=0来确定两个 b i b_{i} bi是否合法
      • 方法二:
        • 设不是 f f f的两个数为 b 1 , b 2 b_{1},b_{2} b1,b2 b 1 − k , b 2 + k b_{1}-k,b_{2}+k b1k,b2+k f f f的倍数,则有 b 1 ≡ k ( m o d f ) , b 2 ≡ ( − k ) ( m o d f ) b_{1}\equiv k\pmod{f},b_{2}\equiv(-k)\pmod{f} b1k(modf),b2(k)(modf)
        • 两式相加得 ( b 1 + b 2 ) ≡ 0 ( m o d f ) (b_{1}+b_{2})\equiv0\pmod{f} (b1+b2)0(modf),即 ( b 1 + b 2 ) % f = 0 (b_{1}+b_{2})\%f=0 (b1+b2)%f=0
        • 因此可以通过判别式 ( b 1 + b 2 ) % f = 0 (b_{1}+b_{2})\%f=0 (b1+b2)%f=0来判断两个 b i b_{i} bi是否合法

特别需要注意的是,由于差分可能会导致负数,所以gcd函数返回的时候需要加绝对值
这与给传入的变量加绝对值是完全不一样的操作!

代码实现

#include<iostream>
#include<cstdio>
#include<vector>
#include<algorithm>
#include<queue>
#include<cmath>
#include<unordered_map>
#include<set>
using namespace std;
using ll = long long;
#define rep(i, a, b) for(ll i = (a); i <= (b); i ++)
#define per(i, a, b) for(ll i = (a); i >= (b); i --)
//#define see(stl) for(auto&ele:stl)cout<<ele<<" "; cout<<'\n';
const ll inf = 1e7 + 5;
// #define int ll
const int N=1e5+5;
 
ll gcd(ll a,ll b){
    if(!b)return abs(a);
    return gcd(b,a%b);
}

int a[N],b[N];
void eachT() {
    int n;cin>>n;
    bool same=1;
    int g1;
    rep(i,1,n){
        cin>>a[i],b[i]=(a[i]-a[i-1]);
        if(i==2)g1=b[2];
        if(i>=3)g1=gcd(g1,(b[i]));
        if(i>=2&&a[i]!=a[i-1])same=0;
    }
    if(same){
        cout<<0<<'\n';return;
    }
    if(n==2){
        cout<<max(a[1],a[2])<<'\n';return;
    }
    set<int>fac;
    for(int i=1;i*i<=a[1];i++){
        if(a[1]%i==0)fac.insert(i),fac.insert(a[1]/i);
    }
    for(int i=1;i*i<=a[n];i++){
        if(a[n]%i==0)fac.insert(i),fac.insert(a[n]/i);
    }
    int cnt,ans=g1;
    for(auto&f:fac){
        int mod=0;
        cnt=0;  
        rep(i,2,n)if(b[i]%f!=0)cnt++,mod+=b[i]%f;
        if(cnt<=1){
            ans=max(ans,f);      
        }
        if((cnt==2)&&(mod%f==0)){
            ans=max(ans,f);
        }
    }
    cout<<ans<<'\n';
}
signed main()
{
    ios::sync_with_stdio(false);
    cin.tie(0), cout.tie(0);
    ll t = 1;
    cin >> t;
    while (t--) { eachT(); }
}


D.漂亮矩阵

#数学 #组合数 #广义二项式定理 #FFT

题目

![[Pasted image 20250802183420.png]]

思路

B i , j = A i , j + 1 − A i , j B_{i,j}=A_{i,j+1}-A_{i,j} Bi,j=Ai,j+1Ai,j,则有 B i , j ≥ B i + 1 , j B_{i,j}\geq B_{i+1,j} Bi,jBi+1,j ∑ j = 1 n B i , j ≤ m \sum_{j=1}^nB_{i,j}\leq m j=1nBi,jm
因此有 ∑ j = 1 n B i + 1 , j ≤ ∑ j = 1 n B i , j ≤ ∑ j = 1 n B 1 , j ≤ m \sum_{j=1}^nB_{i+1,j}\leq \sum_{j=1}^nB_{i,j}\leq \sum_{j=1}^nB_{1,j}\leq m j=1nBi+1,jj=1nBi,jj=1nB1,jm
所以只需要满足 ∑ 1 , j n B 1 , j ≤ m \sum_{1,j}^nB_{1,j}\leq m 1,jnB1,jm且每列的 B B B值单调不增即可

B 1 , i = x B_{1,i}=x B1,i=x,一列共有 n n n个元素,求这一列开头为 x x x且后续单调不增的所有情况数:

  • 假设有 x x x块隔板 l i , i ∈ [ 0 , x ] l_{i},i\in[0,x] li,i[0,x],若 l i − 1 l_{i-1} li1 l i l_{i} li间有 k k k个数,则令这 k k k个数为 i i i
  • 由于开头已经固定为 x x x,只能对剩下的 n − 1 n-1 n1个数进行操作, n − 1 n-1 n1个数提供 n − 1 n-1 n1个空位,再加上 x x x个隔板自己的位置,相当于一共 x + n − 1 x+n-1 x+n1个空位中放入 x x x个隔板,即 C x + n − 1 x C_{x+n-1}^x Cx+n1x
    ![[Pasted image 20250802192143.png]]

接下来利用生成函数来构造总和不超过 m m m的所有情况数:

  • 构造函数 F ( x ) = C 0 + n − 1 0 x 0 + C 1 + n − 1 1 x 1 + ⋯ + C m + n − 1 m x m + ⋯ = ∑ i = 0 ∞ C i + n − 1 i x i F(x)=C_{0+n-1}^0x^0+C_{1+n-1}^1x^1+\dots+C_{m+n-1}^mx^m+\dots=\sum_{i=0}^\infty C_{i+n-1}^ix^i F(x)=C0+n10x0+C1+n11x1++Cm+n1mxm+=i=0Ci+n1ixi
  • 构造多项式 F ( x ) ⋅ F ( x ) ⋅ … ⋅ F ( x ) = F ( x ) n − 1 F(x)·F(x)·\dots·F(x)=F(x)^{n-1} F(x)F(x)F(x)=F(x)n1,相当于除了第一列以外的 n − 1 n-1 n1列的所有情况。多项式 F ( x ) n − 1 F(x)^{n-1} F(x)n1中的前 m m m项的系数之和即为总和不超过 m m m的所有情况数

此时可以用 F F T FFT FFT来快速得出答案,复杂度 o ( n log ⁡ n ) o(n\log n) o(nlogn)

但是其实还可以用广义二项式定理进一步化简:
广义二项式定理实际上就是将组合数的定义域拓宽到了整个实数域
F ( x ) = ∑ i = 0 ∞ C i + n − 1 i x i = ( 1 − x ) − n     ( 广义二项式定理 ) F ( x ) n − 1 = ( 1 − x ) − n ( n − 1 ) 令 t = n ( n − 1 ) 则 F ( x ) n − 1 = ( 1 − x ) − t = ∑ i = 0 ∞ C i + t − 1 i x i = ∑ i = 0 ∞ C i + t − 1 t − 1 x i 则其前 m 项的系数和为 ∑ i = 0 m C i + t − 1 t − 1 = C m + t t 即求 ( m + t ) ! t ! ⋅ m ! = ( m + t ) ( m + t − 1 ) ⋅ ⋅ ⋅ ( t + 1 ) m ! \begin{align} &F(x)=\sum_{i=0}^\infty C_{i+n-1}^ix^i=(1-x)^{-n}\ \ \ (广义二项式定理)\\ \\ &F(x)^{n-1}=(1-x)^{-n(n-1)}\\ \\ &令t=n(n-1) \\ \\ &则F(x)^{n-1}=(1-x)^{-t} =\sum_{i=0}^\infty C_{i+t-1}^ix^i=\sum_{i=0}^\infty C_{i+t-1}^{t-1}x^i\\ \\ &则其前m项的系数和为\sum_{i=0}^mC_{i+t-1}^{t-1}=C_{m+t}^t\\ \\ &即求 \frac{(m+t)!}{t!·m!}= \frac{(m+t)(m+t-1)···(t+1)}{m!} \end{align} F(x)=i=0Ci+n1ixi=(1x)n   (广义二项式定理)F(x)n1=(1x)n(n1)t=n(n1)F(x)n1=(1x)t=i=0Ci+t1ixi=i=0Ci+t1t1xi则其前m项的系数和为i=0mCi+t1t1=Cm+tt即求t!m!(m+t)!=m!(m+t)(m+t1)⋅⋅⋅(t+1)
分子可以 o ( m ) o(m) o(m)暴力算出,分母可以 o ( m ) o(m) o(m)预处理逆元算出,总复杂度为 o ( m ) o(m) o(m)

代码实现

#include<iostream>
#include<cstdio>
#include<vector>
#include<algorithm>
#include<queue>
#include<cmath>
#include<unordered_map>
#include<set>
using namespace std;
using ll = long long;
#define rep(i, a, b) for(ll i = (a); i <= (b); i ++)
#define per(i, a, b) for(ll i = (a); i >= (b); i --)
//#define see(stl) for(auto&ele:stl)cout<<ele<<" "; cout<<'\n';
const ll inf = 1e7 + 5;
// #define int ll
const int N=1e5+5;
 
ll mod=998244353;
ll qpow(ll a, ll b) {
	a %= mod; ll res = 1;
	while (b) {
		if (b % 2) { res *= a, res %= mod; }
		a *= a, a %= mod, b /= 2;
	}
	return res%mod;
}
vector<ll>a, inv;
void inv0(ll len) {
	a.resize(len + 1), inv.resize(len + 1);
	a[0] = 1, inv[0] = 1;
	rep(i, 1, len) {
		a[i] = a[i - 1] * i % mod;
		inv[i] = qpow(a[i], mod - 2);
	}
}

void eachT() {
    ll n,m;cin>>n>>m;
    ll ans=1;
    rep(i,1,m){
        ans*=(n*(n-1)+i)%mod;
        ans%=mod;
    }
    ans*=inv[m];
    ans%=mod;
    cout<<ans<<'\n';
}
signed main()
{
    ios::sync_with_stdio(false);
    cin.tie(0), cout.tie(0);
    inv0(5e5);
    ll t = 1;
    //cin >> t;
    while (t--) { eachT(); }
}

网站公告

今日签到

点亮在社区的每一天
去签到