洛谷 P4831 Scarlet loves WenHuaKe

一题多解

这题首先可以DP
直接三个类型暴力转移。
\(f[i][j]\)表示无限制填\(i\)\(j\)列的方案数。
\(f1[i][j]\)表示有一列已经有棋子的方案数。
\(f2[i][j]\)表示有两列有棋子的方案数。
转移一下。复杂度\(O(n*(m-n))\)

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int M=998244353;
const int N=100010;
int fac[N],invfac[N];
int f[N];
int n,m;
int C(int x,int y){
    return (ll)fac[x]*invfac[y]%M*invfac[x-y]%M;
}
int fp(int x,int y){
    int ret=1;
    for (; y; y>>=1,x=(ll)x*x%M)
    if (y&1) ret=(ll)ret*x%M;
    return ret;
}
int sqr(int x){
    return (ll)x*x%M;
}
int MUL(int x,int y){
    return (ll)x*y%M;
}
int F(int,int);
//int debug;
int f__k[N][20];
int F1(int n,int m){
    //++debug;
    //cerr<<"F_!"<<n<<" "<<m<<" "<<n-m<<endl;
    if (n>m) return 0;
    if (n==0) return 1;
    if (m<2) return 0;
    int &ret=f__k[n][m-n];
    if (ret) return ret-1;
    ret=(MUL(MUL(n,m-1),F1(n-1,m-1))+F(n,m-1))%M+1;
    return ret-1;
}
int mm[N][20];
int F2(int n,int m){
    //getchar();
    //cerr<<m-n<<endl;
    if (n>m) return 0;
    if (n==0) return 1;
    if (m<2) return 0;
    int &ret=mm[n][m-n];
    if (ret) return ret-1;
    //cerr<<"FF"<<n<<" "<<m<<endl;
    ret=F(n,m-2);
    //cerr<<"ORG"<<n<<" "<<m<<" "<<ret<<endl;
    if (n>=2){
      (ret+=MUL(2,MUL(MUL(C(n,2),F2(n-2,m-2)),MUL(m-2,m-3))))%=M;
      (ret+=MUL(2,MUL(MUL(C(n,2),F(n-2,m-3)),m-2)))%=M;
    }
    //cerr<<"ORGG"<<n<<" "<<m<<" "<<ret<<endl;
      if (n>=1)
      (ret+=MUL(2,MUL(F1(n-1,m-2),MUL(C(n,1),m-2))))%=M;
      //cerr<<"OGGGG"<<n<<" "<<m<<" "<<ret<<endl;
      if (n>=1)
      (ret+=MUL(C(n,1),F(n-1,m-2)))%=M;
      //cerr<<"FFRET"<<n<<" "<<m<<" "<<ret<<endl;
      ++ret;
      //++debug;
      return ret-1;
}
int mp[N][20];
int F(int n,int m){
    //cerr<<"F"<<n<<" "<<m<<endl;
    if (n>m) return 0;
    if (n==0) return 1;
    if (m<2) return 0;
    //cerr<<m-n<<endl;
    int &ret=mp[n][m-n];
    if (ret) return ret-1;
    //cerr<<"F"<<n<<" "<<m<<" "<<mp.size()<<endl;
    ret=MUL(C(m,2),F2(n-1,m))+1;
    //if (debug%300==0) cerr<<"FFFF"<<n<<" "<<m<<" "<<ret<<" "<<debug<<endl;
    return ret-1;
}
int FF[2010][2010];
int main(){
    scanf("%d%d",&n,&m);
    if (n<=2000){
    FF[0][0]=1;
    for (int i=0; i<=n; ++i){
        for (int j=0; j<=m; ++j)
        if ((i*2-j)%2==0){
            int k=(i*2-j)/2;
            //cerr<<i<<" "<<j<<" "<<k<<" "<<f[i][j][k]<<endl; 
            int z0=m-k-j,c=FF[i][j];
            //cerr<<i<<" "<<j<<" "<<z0<<endl;
            if (j>=1&&z0>=1) (FF[i+1][j]+=(ll)j*z0%M*c%M)%=M;
            if (z0>=2) (FF[i+1][j+2]+=((ll)z0*(z0-1)/2)%M*c%M)%=M;
            if (j>=2) (FF[i+1][j-2]+=((ll)j*(j-1)/2)%M*c%M)%=M;
        }
    }
    int ans=0;
    for (int i=0; i<=m; ++i){
        //cerr<<i<<" "<<f[n][i]<<endl;
        (ans+=FF[n][i])%=M;
    }
    cout<<ans;
    return 0;
    }
    fac[0]=1;
    for (int i=1; i<=m; ++i) fac[i]=(ll)fac[i-1]*i%M;
    invfac[m]=fp(fac[m],M-2);
    for (int i=m-1; i>=0; --i) invfac[i]=(ll)invfac[i+1]*(i+1)%M;
    //cerr<<invfac[2]<<" "<<invfac[3]<<endl;
    cout<<F(n,m)<<endl;
    //cerr<<debug<<endl;
}

还可以用题解的反演,没有实现过。

猜你喜欢

转载自www.cnblogs.com/Yuhuger/p/10225706.html
今日推荐