首页 > 其他分享 >【DSY 4484】矩阵 题解(带限错排)

【DSY 4484】矩阵 题解(带限错排)

时间:2023-07-05 20:56:12浏览次数:46  
标签:带限 DSY int 题解 位置 矩阵 maxn 错排

DSY 传送门

(带限制)错排问题。

神仙题。

Solution

  • 根据题目的问法,发现我们只想统计比给定矩阵 \(A\) 小的矩阵,记这个矩阵为 \(B\)。
    显然,\(B\) 的第 \(i\) 行一定从某个位置开始小于 \(A\) 的第 \(i\) 行,且前面的内容和 \(A\) 都是一样的。
    所以我们只需要枚举这个“开始变小”的位置然后统计每种情况的答案即可。
    这样枚举的复杂度是 \(O(n^2)\) 的,可以接受。
  • 显然,第 \(i\) 行之后的行,可以随便排,只要满足是“好的矩阵”,它一定比 \(A\) 小。
    根据题目要求“竖直方向上相邻不同”,我们知道这题本质就是一个错排问题。
    也即是说,第 \(i+1\) 到 \(n\) 行随意排的总方案数就是 \(f_n^{n-i}\),其中 \(f_n\) 为 \(n\) 个数的错排方案数。
    且显然,\(f_i=(f_{i-1}+f_{i-2})\times (i-1)\),而这个式子我们已经在 GDOI2023 Day1 讲过。
  • 再考虑第 \(i\) 行的方案如何统计。不妨假设我们在 \((i,j)\) 这个位置开始变小。
    需要注意的是此时第 \(i\) 行和第 \(i-1\) 行是不同的,而且我们还要保证竖直上相邻不同。
    所以这就是个带限制的错排问题——有些数可以放在任意位置,但有些数不能放在特定的某一位置上。
    因为 \([A_{i-1,j},A_{i-1,n}]\) 的数在 \([A_{i,j},A_{i,n}]\) 是不能出现在特定位置的。相反,\([A_{i-1,1},A_{i-1,j-1}]\) 的数在 \([A_{i,j},A_{i,n}]\) 可以放任意位置。
    所以我们倒序使用树状数组统计这些数然后倒序统计方案数。
    具体地,记 \(g_{i,j}\) 表示 \(i\) 个数排列,且有 \(j\) 个数有限制的排列方案数。
    经过简单推导,可得 \(g_{i,j}=g_{i,j-1}-g_{i-1,j-1}\)。
  • 所以最终复杂度为 \(O(n^2\log n)\)。

Code

树状数组的统计见注释说明。代码量大概是 \(2k\)。

#include<bits/stdc++.h>
using namespace std;

#define int long long
#define rep(i, a, b) for(int i = a; i <= b; ++i)
#define per(i, a, b) for(int i = a; i >= b; --i) 
const int mod = 998244353, maxn = 2005, _ = 2000;
int n, a[maxn][maxn];
int fac[maxn], f[maxn], g[maxn][maxn];
int A[maxn][maxn];
int ans, tmp;

struct BIT{
	int t[maxn];
	BIT(){ memset(t, 0, sizeof t);}
	inline int lb(int x){ return x & (-x);}
	inline void add(int x, int k){
		for(int i = x; i <= n; i += lb(i)) (t[i] += k) %= mod;
	}
	inline int qry(int x){ int res = 0;
		for(int i = x; i; i -= lb(i)) (res += t[i]) %= mod;
		return res; 
	}
	inline bool ext(int x){//判断是否统计过数 x 
		return qry(x) - qry(x - 1) != 0 ? 1 : 0;
	} 
};

inline void init(){
	f[0] = fac[0] = 1;
	rep(i, 1, _) fac[i] = fac[i - 1] * i % mod;//预处理全排列方案数 
	rep(i, 2, _) f[i] = (f[i - 1] + f[i - 2]) % mod * (i - 1) % mod;
	rep(i, 0, _){ g[i][0] = f[i];
		rep(j, 1, i)
			g[i][j] = (g[i][j - 1] - g[i - 1][j - 1]) % mod;
	}
}
inline int pw(int x, int p){
	int res = 1;
	while(p){
		if(p & 1) res = res * x % mod;
		p /= 2, x = x * x % mod;
	} return res;
}

signed main(){
	scanf("%lld", &n), init();
	rep(i, 1, n) rep(j, 1, n) scanf("%lld", &a[i][j]);
	BIT t0;//值域树状数组 
	per(i, n, 1){//特殊处理第一行(没有上一行的限制) 
		(tmp += t0.qry(a[1][i]) * fac[n - i] % mod) %= mod;
		t0.add(a[1][i], 1);
	} 
	(ans += tmp * pw(f[n], n - 1) % mod) %= mod;
	rep(i, 2, n){
		BIT t1, t2, t3; tmp = 0;//值域树状数组 
		//t1:这一样出现过的数
		//t2:上一行出现过的数
		//t3:两行都出现过的数 
		per(j, n, 1){
			int cnt = t3.qry(n), flg = 0;//cnt:(倒序)这一行和上一行都出现过的数的个数 
			if(t2.ext(a[i][j])) cnt += 1;
			if(t1.ext(a[i - 1][j])) flg = 1, t1.add(a[i - 1][j], -1);//竖直方向上相邻的位置数不能相同 
			(tmp += t3.qry(a[i][j]) * g[n - j][cnt - 1] % mod) %= mod;//这个位置放一个两行都出现过的数 
			(tmp += (t1.qry(a[i][j]) - t3.qry(a[i][j])) * g[n - j][cnt] % mod) %= mod;
			//这个位置放一个目前只出现过一次的数(无限制) 
			if(flg) t1.add(a[i - 1][j], 1);
			
			//更新3个树状数组 
			t1.add(a[i][j], 1), t2.add(a[i - 1][j], 1);
			if(t2.ext(a[i][j])) t3.add(a[i][j], 1);
			if(t1.ext(a[i - 1][j])) t3.add(a[i - 1][j], 1);
		}
		(ans += tmp * pw(f[n], n - i) % mod) %= mod;
	} printf("%lld", (ans % mod + mod) % mod);
	return 0;
}

Thanks for reading.

标签:带限,DSY,int,题解,位置,矩阵,maxn,错排
From: https://www.cnblogs.com/gsn531/p/17529764.html

相关文章

  • 「NOIP 模拟赛 20230705」T2-序列删数问题 题解
    题面Natsuzora有一个长度为\(n\)的排列\(a_1,a_2,\ldots,a_n\),他想要将序列中的\(m\)个数删除。删除数字需要使用到“魔法工具”,其也有\(m\)种,其中第\(i\)种魔法工具能够将排列中任意一个的长度为\(l_i\)的区间中最大的数删除。每个魔法工具最多只能使用\(1\)次。......
  • 洛谷P9025题解
    P9025题解简化题意求一个值\(c\)使得\[\sum_{i=1}^nw_i(\left|c-p_i\right|-d_i)\]最小化(注意题目中\(w_i\)表示每移动一米需要\(w_i\)秒)思路首先我们令选择\(c\)位置的总用时为\(f(c)\)显然,我们可以把它分成两边来看在\(c\)左边的人:\[f(c)=\sum_{p_i+d_......
  • 题解:【AT icpc2015summer day2-G】 Escape
    题目链接目前AT的最优解。树的话就是根叶链的最大点权和路径,DP随便搞。考虑扩展到图上,反复删除掉所有度数为\(1\)的节点,显然剩下的东西是可以全部取完的,因为它的形态类似于菊花套环,且末端必定为环。将这部分缩起来再跑上面的DP就好了。事实上两部分可以同时进行,一个bfs......
  • Springboot No bean named 'XXXXX' available 问题解决
    一、问题描述近日在工作中遇见了一个bug,后端程序频频报错Nobeannamed'XXXXX'available。对比同类程序文件,没有发现有任何特殊之处。在网上搜索方法基本上就是扫描包配置、注解问题、路径问题等,皆不能解决我的问题。排查问题是发现出现问题的类命名不符合驼峰规范,按照这个......
  • B0704 模拟赛题解
    原题链接前言挂分最多的一场。考虑到之前都无分可挂,这场算是最近很简单的了。T1不排序(按理说我的做法不需要排,但挂了),100->40。T2二分某个边界时单调性判错,100->30。T3原,但没做过。T4某人用模拟退火骗到60ptsorz。T1棍子签到题。考虑二分答案。显然每次要切的......
  • SPOJ Substrings 题解
    \(\text{SAM}\)入门好题。首先我们需要知道几个关于\(\text{SAM}\)的结论。结论1:题目中的\(f(x)\)单调下降。显然,对于长度为\(x\)的子串,其必存在一个\(x-1\)的后缀,这个后缀的\(\text{endpos}\)集合肯定包含子串的\(\text{endpos}\)集合,所以必有\(f(x-1)\le......
  • Hydro #4766. 文艺计算姬 题解--zhengjun
    link前置知识:Prufer序列,二分图别的题解都是直接给答案,没有比较易懂的思路。首先,考虑Prufer序列,发现右边点删除一定会加入一个左边点,另一边类似。且生成Prufer序列的最后一定会留下左右边各一个点。所以左边点在Prufer序列中出现的次数即为\(m-1\),右边即为\(n-1\)。......
  • 【Maven】Unknown lifecycle phase “.test.skip=true“.问题解决
    我们在运行跳过单元测试时的命令mvnpackage-Dmaven.test.skip=true时,出现Unknownlifecyclephase".test.skip=true".如下[ERROR]Unknownlifecyclephase".test.skip=true".Youmustspecifyavalidlifecyclephaseoragoalintheformat<plugin-prefix>......
  • PAT乙级【Java题解合集】
    ✨说在前面       这个暑假博主用大概两周不到的闲暇时间把PAT乙级的110道算法题全部肝完了,个人感觉题目的难度大部分在中等偏下,大概有二十道左右的题目还是蛮有意思的,值得细细去钻研,本专栏非常适合新手入门算法,也适合Java算法老手巩固一些基本知识点,由于C站上关于PAT乙级J......
  • 牛客练习赛 112 B~C题题解
    卡B题了,难受B.qsggandSubarrayB-qsggandSubarray_牛客练习赛112(nowcoder.com)题意给定一个长为n的序列,求有多少个子区间的按位与和为0。思路要想按位与之和为0,那么对于区间的所有数,每个二进制位都要有一个0。设f[i]表示二进制位i的最右边一个0出现的位置,枚举序列的每......