闲话 22.10.6

闲话

所以为什么模拟赛都喜欢考后缀题啊
前有一个SA 后有一个广义SAM上树剖
什么玩意 我只会非字符串的科技(
字符串能不能滚粗OIa

大渣好,我四渣渣辉,点一下,玩一年,装备不花一分钱,说话战斗,罩杯回收,找一基友,极限到手。

0 元 VIP,3 天满级,一秒一刀 999,装备全爆 666,广告做得再牛,不如进服遛一遛!

古天乐绿了,古天乐绿了,惊喜不断,月入上万!不花钱还赚钱的绿色游戏,等级能提现,装备换点钱!

我很奇怪lyin为什么在做核酸时念叨
然后lyin给我指了条明路
什么玩意

所以大家有什么很诡异的题吗 社论没题材了

[最近一直在唱yoasobi那首《怪物》 歌词待补]

杂题

CF1085G

Petya在收集美丽矩阵。一个 \(n\times n\) 的美丽矩阵要保证:

  1. 所有的矩阵内的元素都是1~n的整数
  2. 每一行不能有相同的元素
  3. 每一对竖直相邻的元素不能相同。

Petya定义 稀有度 为将所有 \(n\times n\) 的美丽矩阵按照字典序排好序(从0开始计数)该美丽矩阵所在的编号。他希望你求出他所给的矩阵的稀有度(模 998244353 )。

\(n \le 3000\)

经典炒冷饭环节。

首先题意转化:
1+2:每行是一个 \(1\sim n\) 的排列
3:竖直可能需要广义错排
然后字典序一眼想到 Cantor 展开,但是 Cantor 也没说过矩阵怎么展开啊(

我们规定竖直方向是上面限制下面,这样第一排是没有限制的。我们可以通过 Cantor 展开来确定第一行的排名。现在拓展。

我们枚举 \(b\) 为字典序小于当前矩阵 \(a\) 的矩阵,直到 \(b_{i,j}\) 开始存在不同。
\(b_{i,j} \in [1,n]\)\(\neq \forall k < j,b_{i,k}\)\(\neq a_{i-1,j}\) 。然后本行剩下的数就能随便填了,本行以下的数也可以。本行内的数需要满足与上一行不相等,而本行以下的数就是一个\(n数错排 ^ {n-i}\)
我们发现本行内填法不是完全的错排,因为存在一部分数字没有在 \(b_{i,1}\sim b_{i,j}\) 中出现,且在 \(b_{i-1,j+1}\sim b_{i-1,n}\) 中出现了。记这部分数的数量为 \(c\),它们使得这部分的答案是限制了 \(c\) 个位置错开的 \((n-j)数错排\),记答案为 \(f_{n-j,c}\)

考虑 \(f_{i,j}\) 的生成。
\(i = j\) 是经典错排。
不妨设 \(i > j\),且第 \(i\) 个位置没有限制。如果放 \(j\) 个有限制的数上去转移到 \(f_{i-1,j-1}\),放 \((i-j)\) 个没有限制的数上去转移到 \(f_{i-1,j}\)
于是有递推。

这个东西直接乘进去就行了。但是如果每次都扫一遍的话是 \(O(n^3)\) 的。
考虑我们需要支持查询没有在 \(b_{i,1}\sim b_{i,j}\) 中出现且在 \(b_{i-1,j+1}\sim b_{i-1,n}\) 中出现的数字,支持给定 \(V\) 查询 \(\le V\) 的数字数量。使用 BIT 优化即可。

code
#include <bits/stdc++.h>
#define rep(i,a,b) for ( register int (i) = (a); (i) <= (b); ++(i) )
#define pre(i,a,b) for ( register int (i) = (a); (i) >= (b); --(i) )
using namespace std;
const int mod = 998244353, N = 2e3 + 10;
int n, t1, a[N][N], f[N][N], pw[N], tmp1[N], tmp2[N];
int ans, tot;

struct BIT{
	int Index[N];
	inline void add(int p, int v) {
		if (p == 0) return;
		for ( ; p <= n ; p += p & -p) Index[p] += v;
	} 
	inline int qry(int p) {
		int ret = 0;
		for ( ; p ; p &= p-1)  ret += Index[p];
		return ret;
	}
	inline void clear() {
		rep(i,1,n) Index[i] = 0;
	}
} frt, bk;

typedef long long ll; typedef __int128 lll;
struct FastMod {
    int m; ll b;
    void init(int _m) { m = _m; b = ((lll)1<<64) / m; }
    int operator () (ll a) {
        ll q = ((lll) a * b) >> 64;
        a -= q * m;
        if (a >= m) a -= m;
        return a;
    }
} Mod;
int add(int a, int b) { return (a += b) >= mod ? a - mod : a; }
int mul(int a, int b) { return Mod(1ll * a * b); }
template<typename ...Args> int mul(int a, Args ...b) { return mul(a, mul(b...)); }

signed main() {
    ios::sync_with_stdio(false); cin.tie(0), cout.tie(0);
	cin >> n; pw[0] = 1; f[0][0] = 1; Mod.init(mod);
	rep(i,1,n) rep(j,1,n) cin >> a[i][j];

	rep(i,1,n) {
		f[i][0] = mul(i, f[i-1][0]);
		rep(j,1,i) {
			if(i == j) f[i][i] = mul(i == 1 ? 0 : 1ll * (i-1), add(f[i-1][i-1], f[i-2][i-2]));
			else f[i][j] = add(mul(j, f[i-1][j-1]), mul(i-j, f[i-1][j]));
		}
	} 
    rep(i,1,n) pw[i] = mul(pw[i-1], f[n][n]);
	
	rep(i,1,n) { 
		int l = n;
		rep(i,1,n) tmp1[i] = tmp2[i] = 0;
		frt.clear(), bk.clear();
		rep(i,1,n) bk.add(i, 1);
		long long tmp = 0;
		rep(j,1,n) {
			if (tmp2[a[i-1][j]] == 0) {
				l--; bk.add(a[i-1][j], -1);
			} 
            tmp = add(tmp, mul(frt.qry(a[i][j] - 1), f[n - j][i > 1 ? l : 0]));
			if (l != 0 or i == 1) tmp = add(tmp, mul(bk.qry(a[i][j]-1), f[n - j][i > 1 ? l - 1 : 0]));
			tmp1[a[i][j]] == 0 ? bk.add(a[i][j], -1) : frt.add(a[i][j], -1);
			tmp1[a[i-1][j]] = tmp2[a[i][j]] = 1;
			if (tmp2[a[i-1][j]] == 0) frt.add(a[i-1][j], 1);
			if (tmp1[a[i][j]] == 0) l--;
		} ans = add(ans, mul(tmp, pw[n-i]));
	} cout << ans;
}
posted @ 2022-10-06 19:52  joke3579  阅读(74)  评论(5编辑  收藏  举报