挺高妙一个题。
首先这种看方案数的,又互相限制的肯定找限制最少的,那么肯定是横着的最外面一条和竖着的最外面一条。
若\(l_n< m\),则两者互相独立。否则两者都可能拦住另一个,并且会出现两种不同的结果。
我们设\(f_{i,j}\)表示横着从外到内第\(i\)条,竖着第\(j\)条的方案数,由上述可以得到一个\(O(nm)\)的dp:
若\(l_{n-i+1}<m-j+1,f_{i,j}=f_{i-1,j}\),否则\(f_{i,j}=f_{i-1,j}+f_{i,j-1}\)。
考虑到对\(j\)的值域很大,我们对\(j\)考虑。因为只有\(O(n)\)段转移方式不同,而第一种转移可以直接忽略,因此只剩下第二种,可以看成路径计数问题,预处理组合数即可。
时间复杂度\(O(n^3)\)
code:
#include<bits/stdc++.h>
#include<iostream>
#define Gc() getchar()
#define Me(x,y) memset(x,y,sizeof(x))
#define Mc(x,y) memcpy(x,y,sizeof(x))
#define d(x,y) ((m)*(x-1)+(y))
#define R(n) (rnd()%(n))
#define Pc(x) putchar(x)
#define LB lower_bound
#define UB upper_bound
#define PB push_back
using ll=long long;using db=double;using lb=long db;using ui=unsigned;using ull=unsigned ll;
using namespace std;const int N=5e2+5,M=N*4+5,K=1e5+5,mod=998244353,Mod=mod-1,INF=2e9+7;const db eps=1e-5;mt19937 rnd(time(0));
int n,m,k,x,y,z,A[N],B[N],Bh,vis[N],Sum[N];ll f[N],g[N],Ans,Inv[N],frc[N];
ll mpow(ll x,int y=mod-2){ll Ans=1;while(y) y&1&&(Ans=Ans*x%mod),y>>=1,x=x*x%mod;return Ans;}
ll C(int y){return frc[y]*Inv[y]%mod;}
int main(){
freopen("1.in","r",stdin);
int i,j,h;scanf("%d%d",&m,&n);for(Inv[0]=i=1;i<=n;i++) scanf("%d",&A[i]),A[i]=min(A[i],m),B[++Bh]=m-A[i],Inv[i]=Inv[i-1]*mpow(i)%mod;B[0]=0;B[++Bh]=m;
sort(B,B+Bh+1);Bh=unique(B,B+Bh+1)-B-1;for(i=0;i<=n;i++) f[i]=1;for(i=1;i<=Bh;i++){
for(frc[0]=j=1;j<=n;j++) frc[j]=frc[j-1]*(B[i]-B[i-1]-1+j)%mod;
Me(vis,0);vis[0]=Sum[0]=1;for(j=1;j<=n;Sum[j]=Sum[j-1]+vis[j],j++) if(A[n-j+1]>=m-B[i]+1) vis[j]=1;Mc(g,f);Me(f,0);
for(j=0;j<=n;j++)if(vis[j])for(h=0;h<=j;h++) if(vis[h]) f[j]=(f[j]+g[h]*C(Sum[j]-Sum[h]))%mod;
for(j=0;j<=n;j++) if(!vis[j]) f[j]=f[j-1];
if(B[i]==m)printf("%d\n",f[n]),exit(0);
//for(j=1;j<=m;j++) if(A[n-i+1]>=m-j+1) f[i][j]=(f[i-1][j]+f[i][j-1])%mod;else f[i][j]=f[i-1][j];
}
}
标签:11,UER,int,ll,Ans,using,UOJ,mod,define
From: https://www.cnblogs.com/275307894a/p/16911088.html