CF1805G 题解
DaiRuiChen007 · · 题解
CF1805G 题解
题目大意
定义一个
n\times n 矩阵是好的,当且仅当:
- 矩阵的每一行都是一个
1\sim n 的排列。- 每一对竖直相邻的元素不同。
给定一个好的矩阵,求有多少好矩阵字典序比它小。
数据范围:
n\le 2000 。
思路分析
考虑枚举第一个产生不同的位置
然后考虑这一行的后
不妨设
注意
那么接下来的问题是如何动态维护填
注意到填
先维护
时间复杂度
代码呈现
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int MAXN=2005,MOD=998244353;
ll f[MAXN][MAXN],pw[MAXN],ans=0;
int n,a[MAXN][MAXN];
bool lst[MAXN],cur[MAXN];
struct FenwickTree {
int s,tr[MAXN];
void init() { for(int x=1;x<=n;++x) tr[x]=x&-x; }
void del(int x) { for(;x<=n;x+=x&-x) --tr[x]; }
int qry(int x) { for(s=0;x;x&=x-1) s+=tr[x]; return s; }
} rem,lim;
signed main() {
scanf("%d",&n);
for(int i=1;i<=n;++i) for(int j=1;j<=n;++j) scanf("%d",&a[i][j]);
f[0][0]=1;
for(int i=1;i<=n;++i) for(int j=0;j<=i;++j) {
if(i==j) {
f[i][i]=(i-1)*(f[i-1][i-1]+(i>1?f[i-2][i-2]:0))%MOD;
} else {
f[i][j]=((j?j*f[i-1][j-1]:0)+(i-j)*f[i-1][j])%MOD;
}
}
pw[0]=1;
for(int i=1;i<=n;++i) pw[i]=pw[i-1]*f[n][n]%MOD;
rem.init();
for(int i=1;i<n;++i) {
ans=(ans+rem.qry(a[1][i]-1)*f[n-i][0]%MOD*pw[n-1])%MOD;
rem.del(a[1][i]);
}
for(int i=2;i<=n;++i) {
rem.init(),lim.init();
fill(lst+1,lst+n+1,1);
fill(cur+1,cur+n+1,1);
for(int j=1;j<n;++j) {
if(cur[a[i-1][j]]) lim.del(a[i-1][j]);
lst[a[i-1][j]]=0;
if(j>1) {
if(lst[a[i][j-1]]) lim.del(a[i][j-1]);
cur[a[i][j-1]]=0;
}
int p=lim.qry(n),x=rem.qry(a[i][j]-1),y=lim.qry(a[i][j]-1);
if(a[i-1][j]<a[i][j]&&cur[a[i-1][j]]) --x;
ans=(ans+(x-y)*f[n-j][p]%MOD*pw[n-i])%MOD;
if(p) ans=(ans+y*f[n-j][p-1]%MOD*pw[n-i])%MOD;
rem.del(a[i][j]);
}
}
printf("%lld\n",ans);
return 0;
}