LOJ #2719. 「NOI2018」冒泡排序
题目叙述
求字典序严格比 \(p\) 大的序列中满足这个序列冒泡排序需要的次数等于 \(\sum_{i=1}^n|i-p_i|/2\) 的排列数量。\(p\) 是给定排列。
题解
考虑这个东西相当于,调整的时候不能够折返。
比如 10,9,8 这样东西,就必须折返。
那就是说不能够有长度 \(\ge 3\) 的下降子序列。
所以考虑动态规划,设 \(f_{i,j}\) 表示前 \(i\) 个数中最大的数为 \(j\) 。
有人说这样不行,有可能会考虑到相同的情况。就是说动态规划的状态里面就得记录一个哪些数用过这样的状态。
我说即便记录了最大值,其实也可以考虑相对大小关系。
考虑 \(f_{i,j}\) 能够转移到的状态。有几种情况:
- 加的这个数是最大的。这样的话就没有什么限制。
- 加的数不是最大的。如果不是最大的,就必须是没有加过的数中最小的。否则的话这个数加完之后剩下的数的最小值就比这个数更小了。与前面最大的数就形成了一个长度为 3 的下降子序列。
可以发现转移就是 \(f_{i,j}=\sum_{k=1}^jf_{i-1,k}\) 。
这个东西在我们做格路计数的时候经常有这样的算式。
如果从 \((0,0)\) 出发,到 \((x,y)\) 的方案书为 \(f_{x,y}\) ,那么 \(f_{x,y}\) 也满足类似的递推式。
因此考虑将这个东西转化为格路计数。
但是这个东西和格路计数不同之处在于 \(f_{i,j}\) 这个状态有一个要求是 \(i\le j\) 。
就相当于格路计数的同时不能触碰到某个对角线。
这就直接折线法做掉就可以了。
但是仍然有一个问题是这个题目要求的是固定前缀的情况下剩下的后面的东西的方案数。
仔细观察,发现其实就是前 \(i\) 个数最大值 \(\ge j\) ,剩下的方案数类似的东西。
但前缀的影响也不完全是最大值,要考虑前缀形成的长度为 \(2\) 的下降子序列相关的事情。
分类讨论一下即可。
总结
- 遇到这样 \(f_{i,j}=\sum_{k=1}^jf_{i-1,k}\) 的递推式,要反应过来时格路计数。
- 这个题犯了一些问题:
- 开始以为等价是对于任意 \(p_i=i\) 的位置作为分界点,左边的必须都比他小,右边的数必须必须比他大。
- 事实上这是一个必要条件,却不是充分条件。转化的同时必须要思考是否充分必要!
- 还有一个问题是,对于那个折线法,开始边界条件(指 +1-1 类似的东西)搞错了。因为我举了一个例子,然后依据例子计算的边界条件,但是计算例子的时候没有写到纸上计算,就算错了。解决方案是把东西写到纸上!。
代码
#include <cstdio>
#include <iostream>
#include <algorithm>
#define macro_expand(x) #x
#define print_macro(x) printf("%s\n",macro_expand(x))
#define FOR(i,l,r) for(int i=(l),i##ADJK=(r);i<=i##ADJK;++i)
#define ROF(i,r,l) for(int i=(r),i##ADJK=(l);i>=i##ADJK;--i)
using namespace std;
typedef long long LL;
const int MN=6e5+5,RN=6e5,Mod=998244353;
int ad(int x,int y){return ((x+y)>=Mod)?(x+y-Mod):(x+y);}
int dc(int x,int y){return ((x-y)<0)?(x-y+Mod):(x-y);}
int ml(int x,int y){return (LL)x*y%Mod;}
int add(int &x,int y){return ((x+=y)>=Mod)?(x-=Mod):x;}
int dec(int &x,int y){return ((x-=y)<0)?(x+=Mod):x;}
int ksm(int x,int y){
int ret=1;
for(;y;y>>=1,x=ml(x,x))if(y&1)ret=ml(ret,x);
return ret;
}
int T,N,A[MN],fac[MN*2],caf[MN*2];
void init(){
fac[0]=1;
FOR(i,1,RN*2)fac[i]=ml(fac[i-1],i);
caf[RN*2]=ksm(fac[RN*2],Mod-2);
ROF(i,RN*2-1,0)caf[i]=ml(caf[i+1],i+1);
}
int binom(int u,int d){
if(d<0||u<d)return 0;
return ml(fac[u],ml(caf[d],caf[u-d]));
}
int calc(int i,int j){return dc(binom(2*N-i-j,N-i),binom(2*N-i-j,N+2-i));}
int main(){
freopen("inverse.in","r",stdin);
freopen("inverse.out","w",stdout);
init();
scanf("%d",&T);
while(T--){
scanf("%d",&N);
FOR(i,1,N)scanf("%d",&A[i]);
int premax=0,ans=0,mi=1;
static bool vis[MN];
FOR(i,1,N)vis[i]=0;
FOR(i,1,N){
premax=max(premax,A[i]);
add(ans,calc(i,premax+1));
vis[A[i]]=1;
while(vis[mi])++mi;
if(mi<A[i]&&A[i]<premax)break;
}
printf("%d\n",ans);
}
fclose(stdin);
fclose(stdout);
return 0;
}

浙公网安备 33010602011771号