BZOJ_5416_[Noi2018]冒泡排序_DP+组合数+树状数组

Description

www.lydsy.com/JudgeOnline/upload/noi2018day1.pdf


好题。

合法的排列的交换次数刚好是交换次数的下界,也就是说不能有多余的交换。

也就是对于ai这个数,只能从i到ai这一个方向走。

考虑x,y,z三个数(x>y>z),y需要和x、z各交换一次,这显然不能使y这个数满足只向一个方向移动这个条件。

于是转化为排列的连续下降子序列最多为2。

考虑DP,设f[i][j]表示最后i个数没填,这i个数中有j个数是大于前缀(1~n-i)最大值的方案数。

考虑倒数第i个数可以填什么,f[i][j]<---f[i-1][k],有k<=j。

当k==j时说明填入了一个小于前缀最大值的数,这样的数只可以填最小的一个,有一种方案。

当k<j时0~j-1中的每个k都对应一个比前缀最大值大的数,每个都有一种方案。

有DP式子f[i][j]+=f[i-1][k](0<=k<=j),即f[i][j]=f[i-1][j]+f[i][j-1]。

然后考虑对已知的这个排列怎么做。

假设前缀最大值为mx,现在处理到的这一位排列上的值是p。

如果p>mx,这一位可以从p+1开始取值。

否则,p+1到mx的这些数不能选,如果选了会和前面的mx和后面的p形成3元组。

故这一位考虑max(mx,p)+1开始的数,比较好的一件事就是这些数一定都没出现,那么对答案的贡献就是$\sum\limits_{j=0}^{n-max(mx,p)-1}f[n-i][j]$。

这玩意又等于f[n-i+1][n-max(mx,p)-1]。

还要注意每步如果p<mx,求一下后面还有没有小于p的没填的数,如果有就直接停止,这步用个树状数组搞定。

我们现在的时间复杂度到了$O(n^2)$,发现复杂度瓶颈在求f那里。

考虑dp方程f[i][j]=f[i-1][j]+f[i][j-1],相当于把所有(x,y)(x<y)的格子抠掉,从(0,0)走到(i,j)的方案数。

这玩意就是用推卡特兰数的方式翻折计数可得f[i][j]=C(i+j,i)-C(i+j,i+1)。

然后就做完啦。

代码:

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
#define N 600050
#define mod 998244353
#define _max(x,y) ((x)>(y)?(x):(y))
typedef long long ll;
inline char nc() {
static char buf[100000],*p1,*p2;
return p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++;
}
int rd() {
int x=0; char s=nc();
while(s<'0'||s>'9') s=nc();
while(s>='0'&&s<='9') x=(x<<3)+(x<<1)+s-'0',s=nc();
return x;
}
int fac[N<<1],inv[N<<1],n,p[N],c[N];
int qp(int x,int y) {int re=1;for(;y;y>>=1,x=ll(x)*x%mod) if(y&1) re=ll(re)*x%mod;return re;}
void fix(int x,int v) {for(;x<=n;x+=x&(-x)) c[x]+=v;}
int inq(int x) {int re=0;for(;x;x-=x&(-x)) re+=c[x]; return re;}
void init() {
int i;
for(fac[0]=1,i=1;i<=1200000;i++) fac[i]=ll(fac[i-1])*i%mod;
inv[1200000]=qp(fac[1200000],mod-2);
for(i=1199999;i>=0;i--) inv[i]=ll(inv[i+1])*(i+1)%mod;
}
int C(int n,int m) {
if(n<m) return 0;
return ll(fac[n])*inv[m]%mod*inv[n-m]%mod;
}
int F(int i,int j) {
return (C(i+j,i)-C(i+j,i+1)+mod)%mod;
}
void solve() {
memset(c,0,sizeof(c));
n=rd();
int i;
int ans=0,mx=0;
for(i=1;i<=n;i++) p[i]=rd(),fix(i,1);
for(i=1;i<n;i++) {
int flg=inq(p[i]-1),lim=_max(mx,p[i])+1,cnt=n-lim+1;
ans=(ans+F(n-i+1,cnt-1))%mod;
if(p[i]<mx&&flg) break;
mx=_max(mx,p[i]);
fix(p[i],-1);
}
printf("%d\n",ans);
}
int main() {
init();
int T;
T=rd();
while(T--) solve();
}

BZOJ_5416_[Noi2018]冒泡排序_DP+组合数+树状数组的更多相关文章

  1. LOJ #2719. 「NOI2018」冒泡排序(组合数 + 树状数组)

    题意 给你一个长为 \(n\) 的排列 \(p\) ,问你有多少个等长的排列满足 字典序比 \(p\) 大 : 它进行冒泡排序所需要交换的次数可以取到下界,也就是令第 \(i\) 个数为 \(a_i\ ...

  2. [BZOJ1227][SDOI2009]虔诚的墓主人 组合数+树状数组

    1227: [SDOI2009]虔诚的墓主人 Time Limit: 5 Sec  Memory Limit: 259 MBSubmit: 1433  Solved: 672[Submit][Stat ...

  3. 洛谷 P4375 [USACO18OPEN]Out of Sorts G(树状数组求冒泡排序循环次数加强版)

    传送门:Problem 4375 参考资料: [1]:https://www.cnblogs.com/Miracevin/p/9662350.html [2]:https://blog.csdn.ne ...

  4. [luogu2154 SDOI2009] 虔诚的墓主人(树状数组+组合数)

    传送门 Solution 显然每个点的权值可以由当前点上下左右的树的数量用组合数\(O(1)\)求出,但这样枚举会T 那么我们考虑一段连续区间,对于一行中两个常青树中间的部分左右树的数量一定,我们可用 ...

  5. bzoj1227: [SDOI2009]虔诚的墓主人(树状数组,组合数)

    传送门 首先,对于每一块墓地,如果上下左右各有$a,b,c,d$棵树,那么总的虔诚度就是$C_k^a*C_k^b*C_k^c*C_k^d$ 那么我们先把所有的点都给离散,然后按$x$为第一关键字,$y ...

  6. HDU 1394 Minimum Inversion Number(最小逆序数/暴力 线段树 树状数组 归并排序)

    题目链接: 传送门 Minimum Inversion Number Time Limit: 1000MS     Memory Limit: 32768 K Description The inve ...

  7. hrbust oj 1526+2028 树状数组

    冒泡排序中 如果一个数的后面的某个数和这个数不符合排序规则 那么这个数就会在未来的某次冒泡中与那个数进行交换 这里用到了 树状数组求逆序数的办法来做 需要注意的是2028并不可以改完数组大小后直接套1 ...

  8. 树状数组求逆序对:POJ 2299、3067

    前几天开始看树状数组了,然后开始找题来刷. 首先是 POJ 2299 Ultra-QuickSort: http://poj.org/problem?id=2299 这题是指给你一个无序序列,只能交换 ...

  9. poj 2299 树状数组求逆序对数+离散化

    Ultra-QuickSort Time Limit: 7000MS   Memory Limit: 65536K Total Submissions: 54883   Accepted: 20184 ...

随机推荐

  1. DICOM:DICOM万能编辑工具之Sante DICOM Editor

    版权声明:本文为zssure原创文章,转载请注明出处,未经允许不得转载.   目录(?)[-] 背景 DICOM Service的配置 Sante DICOM Editor自启动的服务 PACS查询下 ...

  2. LeetCode 205 Isomorphic Strings(同构的字符串)(string、vector、map)(*)

    翻译 给定两个字符串s和t,决定它们是否是同构的. 假设s中的元素被替换能够得到t,那么称这两个字符串是同构的. 在用一个字符串的元素替换还有一个字符串的元素的过程中.所有字符的顺序必须保留. 没有两 ...

  3. 从头写一个Cucumber测试(二) Cucumber Test

    转载:https://yaowenjie.github.io/%E7%BC%96%E7%A8%8B%E7%9B%B8%E5%85%B3/cucumber-test-part-2 承接上文   前一篇博 ...

  4. iphone越狱-------平刷回越狱前(未越狱)状态

    众所周知,iPhone采用了沙盒机制,应用之间不能任意的访问,所以很多机友在拿到iPhone后,往往选择进行越狱,但是有时候,越狱的手机有的时候在安全.性能.流畅性等方面表现并不如意,所以只好寻求重新 ...

  5. Java常见面试题汇总(一)

    1)什么是Java虚拟机?为什么Java被称作是"平台无关的编程语言"? Java虚拟机是一个能够运行Java字节码的虚拟机进程.Java源文件被编译成能被Java虚拟机运行的字节 ...

  6. C++编译错误 2001 1120

    无法解析的外部符号"symbol" 代码引用了链接器无法在库和对象文件中找到的内容(如函数.变量或标签). 该错误信息之后为错误 LNK1120. 可能的原因 : 在将托管库或 W ...

  7. Linux 下装mysql[Ubuntu & CentOS]

    Ubuntu: https://blog.csdn.net/hipilee/article/details/77610916

  8. 将Ubuntu主文件夹里的中文文件夹名称改成英文

    方法一: 首先修改现有主文件夹下各文件夹名称: Desktop. Documents. Download. Music. Pictures. Public. Templates. Videos …… ...

  9. 【CUDA】CUDA开发环境搭建

    http://blog.csdn.net/tracer9/article/details/50484764 标签: CUDA并行计算NVIDIAlinux 2016-01-08 18:35 637人阅 ...

  10. bitmap进行个数统计

    昨天看了编程珠玑第一章的内容, 发现bitmap对于统计某一个范围内的整数个数效率很高, 就自己实现了一下: 这是原始的bitmap, 用于统计0~maxSize是否出现, 也可以用于排序 publi ...