把博客园图标替换成自己的图标
把博客园图标替换成自己的图标end

luogu P4769 [NOI2018] 冒泡排序

题面传送门

首先我们来考虑一下题目里的这个性质实际上相当于什么。

注意到 3,2,1 不是一个正确的排列,一个原因就是 2 在交换过程中又向左移动又向右移动,而如果要满足题目限制的话就只能向一个方向移动,也就是说不存在 x<y<z,px>py>pz

根据 Dliworth 定理,这相当于能把原序列划分成两个上升子序列。

这有一个典中典的判定:维护当前的 max,如果新来了一个数大于这个 max,那么将 max 更新成它,否则这个数一定要是没有出现过的最小的。

那么你就可以写出这个式子了:设 fi,j 表示到了第 i 个位置,前缀 maxj 的方案数,那么有fi,j=fi1,j+fi,j1 。特别的,因为 ji,因此 fi,i=fi1,i

这相当于是从某个点出发,不能跨过对角线,走到 (n,n) 的方案数。因为有字典序的限制,所以有 O(n) 次查询。我们需要一个较为快速的计算方法。

这个东西和卡特兰数长得很像。众所周知卡特兰数的公式是 (2nn)(2nn1)。这个东西是怎么推的呢?考虑如果某个点跨过了 y=x 到达终点,也就是和 y=x+1 有交点。将剩下的路径关于 y=x+1 对称。那么就变成 (0,0) 走到 (n1,n+1) 的方案数。就是上面这个式子。

这道题里面也是如此。这可以看作不能跨过 y=x1 ,也就是关于 y=x2 对称。就可以做到 O(1) 回答询问。时间复杂度 O(n)

#include<bits/stdc++.h>
#define Gc() getchar() 
#define Me(x,y) memset(x,y,sizeof(x))
#define Mc(x,y) memcpy(x,y,sizeof(x))
#define d(x,y) ((m)*(x-1)+(y))
#define R(n) (rnd()%(n)+1)
#define Pc(x) putchar(x)
#define LB lower_bound
#define UB upper_bound
#define PB push_back
#define fi first
#define se second
using ll=long long;using db=double;using lb=long db;using ui=unsigned;using ull=unsigned long long;
using namespace std;const int N=1.8e6+5,M=N*5+5,K=N*5,mod=998244353,Mod=mod-1;const db eps=1e-9;const int INF=1e9+7;mt19937 rnd(time(0));
int n,m,k,x,y,z,A[N],L1[N],Fl[N],fa[N];ll Ans,frc[N],Inv[N];
ll mpow(ll x,int y=mod-2){ll Ans=1;while(y) y&1&&(Ans=Ans*x%mod),y>>=1,x=x*x%mod;return Ans;}
int GF(int x){return fa[x]^x?fa[x]=GF(fa[x]):x;}
void Add(int x,int w){
	L1[x]=L1[x-1];if(w>L1[x]) L1[x]=w;else if(w^GF(1)) L1[x]=INF;
}
ll C(int x,int y){if(y<0||y>x)return 0;return frc[x]*Inv[y]%mod*Inv[x-y]%mod;}
ll Go(int x,int y){
	if(x==n) return y==n;
	return C(n-x+n-y,n-x)-C(n-x+n-y,n+2-x);
}
void GA(int x){
	if(L1[x]==INF) return;Ans+=Go(x,L1[x]);
}
void Solve(){
	int i,j;scanf("%d",&n);m=2*n+10;for(i=1;i<=n;i++) scanf("%d",&A[i]),Fl[i]=0,fa[i]=i;
	Ans=0;for(frc[0]=i=1;i<=m;i++) frc[i]=frc[i-1]*i%mod;Inv[m]=mpow(frc[m]);for(i=m-1;~i;i--) Inv[i]=Inv[i+1]*(i+1)%mod;
	for(i=1;i<=n;i++){
		Add(i,max(A[i]+1,L1[i-1]+1)),GA(i);
		Fl[A[i]]=1;Add(i,A[i]);fa[A[i]]=A[i]+1;
	}printf("%lld\n",(Ans%mod+mod)%mod);
}
int main(){
	freopen("1.in","r",stdin);
	int t;scanf("%d",&t);while(t--) Solve();
}
posted @   275307894a  阅读(23)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
历史上的今天:
2021-03-07 关于调和级数的一些思考
浏览器标题切换
浏览器标题切换end
点击右上角即可分享
微信分享提示