一般的套路显然转化按位确定
第一行直接算有多少字典序比他小就行了。
对于\(2\)到\(n\)行,每行的总方案数就是错排数量。
这就存在一个问题,我们无法确定当前行能放的有多少字典序比它小。
我们发现对于第\(i\)行第\(j\)个,假如第\(i-1\)行前\(j\)个数和第\(i\)行前\(j\)个数一共有\(y\)个不同的数,那么在剩下的\(n-j\)个数中,就存在\(y\)个位置不受限制,也就是一共存在\(n-j-y\)个限制,那么现在的问题就是我们需要求出\(f[i][j]\)表示\(i\)个数,存在\(j\)个限制的排列数。
容易得出\(f[i][j]=f[i][j-1]-f[i-1][j-1]\),\(n\)个数\(n\)个限制就是错排。
现在需要考虑的问题是我们需要算出每个位置能放的比\(a[i][j]\)小的数有多少。
这又有两种情况:
我们发现对于第\(i\)行第\(j\)个,假如第\(i-1\)行前\(j\)个一共有\(x\)个比\(a[i][j]\)小的数字在第\(i\)行前\(j-1\)个中没有出现过。
1、那么这个位置放这\(x\)个数就会导致前\(j\)个数一共只剩下\(x-1\)个数不同,也就是后\(n-j\)个数有\(n-j-x+1\)个限制
2、假如不放这\(x\)个数,依然是\(x\)个限制
细节貌似不多,反正我是写自闭了
代码:
#include#include #include #include using namespace std;#define rg registervoid read(int &x){ char ch;bool ok; for(ok=0,ch=getchar();!isdigit(ch);ch=getchar())if(ch=='-')ok=1; for(x=0;isdigit(ch);x=x*10+ch-'0',ch=getchar());if(ok)x=-x;}const int maxn=2010,mod=998244353;bool vis[maxn],used[maxn];int n,a[maxn][maxn],f[maxn][maxn],fac[maxn],ans;int mul(int x,int y){return 1ll*x*y-1ll*x*y/mod*mod;}int add(int x,int y){return x+y>=mod?x+y-mod:x+y;}int del(int x,int y){return x-y<0?x-y+mod:x-y;}int g[2][maxn],ff[maxn];#define lowbit(i) (i&(-i))void ins(int a,int x,int z){for(rg int i=x;i<=n;i+=lowbit(i))g[a][i]+=z;}int get(int a,int x){int ans=0;for(rg int i=x;i;i-=lowbit(i))ans=add(ans,g[a][i]);return ans;}int main(){ read(n);fac[0]=1; for(rg int i=1;i<=n;i++)fac[i]=mul(fac[i-1],i); for(rg int i=1;i<=n;i++) for(rg int j=1;j<=n;j++) read(a[i][j]); f[0][0]=1; for(rg int i=1;i<=n;i++){ f[i][0]=fac[i]; for(rg int j=1;j<=i;j++) f[i][j]=del(f[i][j-1],f[i-1][j-1]); } ff[0]=1; for(rg int i=1;i<=n;i++)ff[i]=mul(ff[i-1],f[n][n]); for(rg int j=1;j<=n;j++){ int t=0; for(rg int i=1;i