咕咕咕到现在~

题面传送门

题意:

给出一个 \(n\times n\) 的矩阵 \(A\)。要你求有多少个 \(n\times n\) 的矩阵 \(B\) 满足:

  • 每一行都是 \(1\) 到 \(n\) 的排列。
  • 对于任意 \(1\leq i\lt n\),\(1\leq j\leq n\),\(B_{i,j}\neq B_{i+1,j}\)
  • 定义 \(f(A)\) 为矩阵 \(A\) 从上到下,从左到右拼接而成的序列,即 \(f(A)_{(i-1)\times n+j}=A_{i,j}\),那么必须有 \(f(B)\) 的字典序小于 \(f(A)\) 的字典序。

    答案对 \(998244353\) 取模。

我们考虑条件三。“\(f(B)\) 的字典序小于 \(f(A)\) 的字典序”意味着我们可以枚举第一个 \(f(A)_i<f(B)_i\) 的位置然后统计答案。

假设第一个不相等的位置为 \(i\) 行 \(j\) 列。那么矩阵 \(B\) 的前 \(i-1\) 行上的数,以及第 \(i\) 行的前 \(j-1\) 个数,都已经确定下来了,我们的任务就是填好剩下来的 \(n^2-(i-1)\times n-(j-1)\) 个数。

不难注意到,对于后 \(n-i\) 行每行的方案数是固定的,即 \(f_n\),其中 \(f_i\) 表示长度为 \(i\) 的满足 \(p_j \neq j\) 的排列个数,也就是一个简单的错排数问题。

接下来考虑如何计算填好第 \(i\) 行剩余的 \(n-j+1\) 个数的方案数,我们可以枚举第 \(j\) 个数是什么。那么我们需把这一行剩下来的 \(n-j\) 个数塞入这 \(n-j\) 个位置。再,假设 \(A_{i-1,j+1},A_{i-1,j+2},\dots,A_{i-1,n}\) 分别记作 \(x_1,x_2,\dots,x_{n-j}\),第 \(i\) 行剩余的数分别记作 \(y_1,y_2,\dots,y_{n-j}\),要求有多少个 \(1\) 到 \(n-j\) 的排列 \(p\) 满足 \(x_i \neq y_{p_i}\)。

其实方案数与 \(x_i,y_i\) 具体是什么数不重要,我们关心的只是有多少个数在 \(x,y\) 中同时出现,也就是官方题解中的“有限制的位置”与“无限制的位置”。

然后就可以 \(dp\) 了,设 \(dp_{i,j}\) 为有 \(i\) 个有限制的位置和 \(j\) 个无限制的位置的方案数。

若 \(j=0\),显然 \(dp_{i,j}=f_i\),直接上公式 \(f_i=\sum\limits_{j=0}^i\binom{i}{j}\times(-1)^{i-j}\)

若 \(j \neq 0\),那么可以用类似组合数递推的方式。枚举最后一个无限制的位置上填的数,如果填了一个无限制的数,有 \(j\) 种方案,无限制的数变为 \(j-1\),即 \(j \times dp_{i,j-1}\);如果填了一个有限制的数,有 \(i\) 种方案,无限制的位置还是 \(j\) 个,原来有限制的数变为无限制的数,即 \(i \times dp_{i-1,j}\)。故 \(dp_{i,j}=j \times dp_{i,j-1}+i \times dp_{i-1,j}\)。

这样做是 \(n^3\) 的。不过可以进一步优化:枚举这个位置填的是有限制的数还是没限制的数,用树状数组/线段树维护选法,乘上一个 \(dp\) 系数即可。

  1. #include <bits/stdc++.h>
  2. using namespace std;
  3. #define fz(i,a,b) for(int i=a;i<=b;i++)
  4. #define fd(i,a,b) for(int i=a;i>=b;i--)
  5. #define fill0(a) memset(a,0,sizeof(a))
  6. #define fill1(a) memset(a,-1,sizeof(a))
  7. #define fillbig(a) memset(a,63,sizeof(a))
  8. #define fi first
  9. #define se second
  10. #define int long long
  11. const int MOD=998244353;
  12. int n,a[2005][2005],f[2005],c[2005][2005],fac[2005],dp[2005][2005],b[2005][2005];
  13. bool vis1[2005],vis2[2005];
  14. int qpow(int x,int e){
  15. int ans=1;
  16. while(e){
  17. if(e&1) ans=ans*x%MOD;
  18. x=x*x%MOD;e>>=1;
  19. }
  20. return ans;
  21. }
  22. struct node{
  23. int l,r,cnt[4],val;
  24. } s[2005<<2];
  25. inline void pushup(int k){
  26. fz(i,0,3) s[k].cnt[i]=s[k<<1].cnt[i]+s[k<<1|1].cnt[i];
  27. }
  28. inline void build(int k,int l,int r){
  29. // cout<<k<<" "<<l<<" "<<r<<endl;
  30. s[k].l=l;s[k].r=r;fill0(s[k].cnt);s[k].val=0;s[k].cnt[0]=r-l+1;
  31. if(l==r) return;
  32. int mid=(l+r)>>1;
  33. build(k<<1,l,mid);build(k<<1|1,mid+1,r);
  34. }
  35. inline void add(int k,int ind,int x){
  36. if(s[k].l==s[k].r){
  37. s[k].cnt[s[k].val]--;s[k].val+=x;s[k].cnt[s[k].val]++;
  38. return;
  39. }
  40. int mid=(s[k].l+s[k].r)>>1;
  41. if(ind<=mid) add(k<<1,ind,x);
  42. else add(k<<1|1,ind,x);
  43. pushup(k);
  44. }
  45. inline int query(int k,int l,int r,int x){
  46. if(l>r||!l||!r) return 0;
  47. if(l<=s[k].l&&s[k].r<=r) return s[k].cnt[x];
  48. int mid=(s[k].l+s[k].r)>>1;
  49. if(r<=mid) return query(k<<1,l,r,x);
  50. else if(l>mid) return query(k<<1|1,l,r,x);
  51. else return query(k<<1,l,mid,x)+query(k<<1|1,mid+1,r,x);
  52. }
  53. signed main(){
  54. scanf("%d",&n);
  55. fz(i,0,n){
  56. c[i][0]=1;
  57. fz(j,1,i) c[i][j]=(c[i-1][j]+c[i-1][j-1])%MOD;
  58. }
  59. fac[0]=1;fz(i,1,n) fac[i]=fac[i-1]*i%MOD;
  60. fz(i,0,n){
  61. fz(j,0,i){
  62. if((j&1)==(i&1)) f[i]=(f[i]+c[i][j]*fac[j]%MOD)%MOD;
  63. else f[i]=(f[i]-c[i][j]*fac[j]%MOD+MOD)%MOD;
  64. }
  65. // cout<<i<<" "<<f[i]<<endl;
  66. }
  67. for(int i=0;i<=n;i++){
  68. dp[i][0]=f[i];
  69. for(int j=1;i+j<=n;j++){
  70. dp[i][j]=(dp[i][j-1]*j%MOD+dp[i-1][j]*i%MOD)%MOD;
  71. // printf("%d %d %d\n",i,j,dp[i][j]);
  72. }
  73. }
  74. fz(i,1,n) fz(j,1,n) scanf("%d",&a[i][j]);
  75. fz(i,1,n-1){
  76. fill0(vis1);fill0(vis2);
  77. int both=0;
  78. fd(j,n,1){
  79. vis1[a[i][j]]=1;if(vis2[a[i][j]]) both++;
  80. vis2[a[i+1][j]]=1;if(vis1[a[i+1][j]]) both++;
  81. b[i][j]=both;
  82. }
  83. // fz(j,1,n) cout<<b[i][j]<<" ";puts("");
  84. }
  85. int ans=0;
  86. fz(i,1,n){
  87. build(1,1,n);int sum=0;
  88. fd(j,n,1){
  89. // cout<<i<<" "<<j<<endl;
  90. add(1,a[i][j],1);if(i!=1) add(1,a[i-1][j],2);
  91. int cnt1=query(1,1,a[i][j]-1,1)-((a[i-1][j]<a[i][j])?(query(1,a[i-1][j],a[i-1][j],1)):0);
  92. int cnt2=query(1,1,a[i][j]-1,3)-((a[i-1][j]<a[i][j])?(query(1,a[i-1][j],a[i-1][j],3)):0);
  93. int flg=query(1,a[i][j],a[i][j],2)+query(1,a[i][j],a[i][j],3);
  94. sum=(sum+cnt2*dp[b[i-1][j+1]-1+flg][n-j-(b[i-1][j+1]-1+flg)]%MOD+cnt1*dp[b[i-1][j+1]+flg][n-j-(b[i-1][j+1]+flg)]%MOD)%MOD;
  95. // cout<<i<<" "<<j<<" "<<cnt1<<" "<<cnt2<<" "<<b[i-1][j]<<endl;
  96. }
  97. ans=(ans+sum*qpow(f[n],n-i)%MOD)%MOD;
  98. }
  99. printf("%lld\n",ans);
  100. return 0;
  101. }

【2020五校联考NOIP #2】矩阵的更多相关文章

  1. 【2020五校联考NOIP #8】自闭

    题目传送门 题意: 有一个 \(n \times m\) 的矩阵,里面已经填好了 \(k\) 个非负整数. 问是否能在其它 \(n \times m-k\) 个格子里各填上一个非负整数,使得得到的矩阵 ...

  2. 【2020五校联考NOIP #6】三格缩进

    题意: 给出 \(n\) 个数 \(a_1,a_2,\dots,a_n\),你要进行 \(m\) 次操作,每次操作有两种类型: \(1\ p\ x\):将 \(a_p\) 改为 \(x\). \(2\ ...

  3. 【2020五校联考NOIP #8】狗

    题面传送门 原题题号:Codeforces 883D 题意: 有 \(n\) 个位置,每个位置上要么有一条狗,要么有一根骨头,要么啥都没有. 现在你要给每个狗指定一个方向(朝左或朝右). 朝左的狗可以 ...

  4. 【2020五校联考NOIP #7】道路扩建

    题面传送门 题意: 给出一张 \(n\) 个点 \(m\) 条边的无向图 \(G\),第 \(i\) 条边连接 \(u_i,v_i\) 两个点,权值为 \(w_i\). 你可以进行以下操作一次: 选择 ...

  5. 【2020五校联考NOIP #4】今天的你依旧闪耀

    题面传送门 题意: 对于一个长度为 \(n\)(\(n\) 为偶数)的排列 \(p\),定义一次"变换"后得到的排列 \(p'\) 为: \(p'_i=\begin{cases}p ...

  6. 【2020五校联考NOIP #3】序列

    题面传送门 原题题号:Codeforces Gym 101821B 题意: 给出一个排列 \(p\),要你找出一个最长上升子序列(LIS)和一个最长下降子序列(LDS),满足它们没有公共元素.或告知无 ...

  7. 【2020五校联考NOIP #7】伟大的卫国战争

    题面传送门 题意: 数轴上有 \(n\) 个点,现在要在它们之间连 \(m\) 条边,第 \(i\) 条边连接 \(a_i,b_i\) 两个点. 现在你要钦定每条边连在数轴的上方还是下方,使得任意两条 ...

  8. 【2020五校联考NOIP #6】最佳观影

    题意: 给出一个 \(k \times k\) 的网格和 \(n\) 次操作.其中 \(k\) 为奇数. 每次操作给出一个数 \(m\).每次你要找出一个三元组 \((x,l,r)\) 使得: \(r ...

  9. 五校联考R1 Day2T2 矩阵matrix(容斥)

    题目链接 容易想到容斥,但是很恶心,因为要对行和列都容斥,然后行+列又要容斥.. 于是得到\(O(nm\log)\)的做法. 就有70分了: #include <cstdio> #incl ...

随机推荐

  1. EMC测试国家标准GB/T 17626

    转载: 详解EMC测试国家标准GB/T 17626 - whik - 博客园 (cnblogs.com)

  2. 初始HTML05

    HTML 表单控件属性 表单控件可设置以下标签属性 属性名 取值 type 设置控件类型 name 设置控件名称,最终与值一并发送给服务器 value 设置控件的值 placeholder 设置输入框 ...

  3. SingleR如何使用自定义的参考集

    在我之前的帖子单细胞分析实录(7): 差异表达分析/细胞类型注释里面,我已经介绍了如何使用SingleR给单细胞数据做注释,当时只讲了SingleR配套的参考集.这次就讲讲如何使用自己定义/找到的基因 ...

  4. ORB_SLAM3 -- 配置安装

    安装环境 Ubuntu20.04 ORB_SLAM3依赖项安装 opencv3 ORB_SLAM3可用opencv3或opencv4编译,作者这里安装测试了opencv3 Step1: 安装openc ...

  5. Sequence Model-week2编程题1-词向量的操作【余弦相似度 词类比 除偏词向量】

    1. 词向量上的操作(Operations on word vectors) 因为词嵌入的训练是非常耗资源的,所以ML从业者通常 都是 选择加载训练好 的 词嵌入(Embedding)数据集.(不用自 ...

  6. [技术博客]大闸蟹的技术博客,通过gitlab api进行用户批量创建

    技术博客--通过gitlab api批量注册用户 gitlab登录界面本身提供了register功能,但需要手工一个个添加,对于一次性会添加整个班级的学生的软工平台来说并不科学合理.使用gitlab ...

  7. Prometheus监控Canal

    Prometheus监控Canal 一.背景 二.实现步骤 1.修改prometheus.yml配置文件 2.启动prometheus 3.查看prometheus是否成功接入canal 4.cana ...

  8. GPIO原理与配置(跑马灯,蜂鸣器,按键)

    一.STM32 GPIO固件库函数配置方法 1. 根据需要在项目中删掉一些不用的固件库文件,保留有用的固件库文件 2. 在stm32f10x_conf.h中注释掉这些不用的头文件 3. STM32的I ...

  9. Ubuntu下在当前用户下安装JDK1.8

    Oracle官网的JDK下载需要用户登录才能下载,JDK1.8的下载地址:https://www.oracle.com/cn/java/technologies/javase/javase-jdk8- ...

  10. Java设计模式——模板设计模式

    模板设计模式 1.模板模式简介 模板模式(Template ):模板方法模式是类的行为模式.准备一个抽象类,将部分逻辑以具体方法以及具体构造函数的形式实现,然后声明一些抽象方法来迫使子类实现剩余的逻辑 ...