QOJ 5047 Permutation

题目传送门

首先注意到一个事情,在 \(1\) 两端的数永远不可能跨过 \(1\)。根据这个东西,我们可以考虑分治。

我们设 \(work(l,r,L,R)\) 表示区间 \([l,r]\),左端有 \(L\) 个数可以随意重排,右边有 \(R\) 个数可以随意重排的方案数,考虑怎么进行计算。

不难注意到,若 \(L,R\) 包含的范围有交或者其中一个包含了整个区间,那这个区间可以随意重排。

不妨设 \(p\)\([l,r]\) 中最小值的位置。考虑对 \(p\) 的位置进行分类讨论。

  • \(p\) 在左右两端包含的范围之间:那么直接删掉 \(p\),分治 \(p\) 的两边。

  • \(p\) 在左端包含的范围内:把这个数换到 \(l\) 的位置,然后删掉 \(l\) 这个位置继续分治。

  • \(p\) 在右端包含的范围内:把这个数换到 \(r\) 的位置,然后删掉 \(r\) 这个位置继续分治。

每种情况之后的 \(L,R\) 都是好处理的,可以自行思考。

AC code:

#include<bits/stdc++.h>
#define int long long
#define N 500005
#define pii pair<int,int>
#define x first
#define y second
#define mod 998244353
#define inf 2e18
using namespace std;
int T=1,n,m,a[N],p[N],f[N];
void init(){
	f[0]=1;
	for(int i=1;i<N;i++){
		f[i]=f[i-1]*i%mod;
	}
}
struct sgt{
	int tr[N<<2];
	void pushup(int u){
		tr[u]=min(tr[u<<1],tr[u<<1|1]);
	}
	void build(int u,int l,int r){
		if(l==r){
			tr[u]=a[l];
			return;
		}
		int mid=l+r>>1;
		build(u<<1,l,mid);
		build(u<<1|1,mid+1,r);
		pushup(u);
	}
	void modify(int u,int l,int r,int p,int v){
		if(l==r){
			tr[u]=v;
			return;
		}
		int mid=l+r>>1;
		if(p<=mid)modify(u<<1,l,mid,p,v);
		else modify(u<<1|1,mid+1,r,p,v);
		pushup(u);
	}
	int qry(int u,int l,int r,int L,int R){
		if(l>=L&&r<=R)return tr[u];
		int mid=l+r>>1;
		int res=inf;
		if(L<=mid)res=min(res,qry(u<<1,l,mid,L,R));
		if(R>mid)res=min(res,qry(u<<1|1,mid+1,r,L,R));
		return res;
	}
}s;
int work(int l,int r,int L,int R){
	if(L+R>r-l+1||max(L,R)>=r-l+1){
		return f[r-l+1];
	}
	int mn=s.qry(1,1,n,l,r);
	int pos=p[mn];
	if(pos>l+L-1&&pos<r-R+1){
		return work(l,pos-1,L,(pos-l>=m)*m)*work(pos+1,r,(r-pos>=m)*m,R)%mod;
	}
	else if(pos<=l+L-1){
		swap(p[a[l]],p[a[pos]]);
		swap(a[l],a[pos]);
		s.modify(1,1,n,l,a[l]);
		s.modify(1,1,n,pos,a[pos]);
		return L*work(l+1,r,min(L+m-1,r-l),R)%mod;
	}
	else{
		swap(p[a[r]],p[a[pos]]);
		swap(a[r],a[pos]);
		s.modify(1,1,n,r,a[r]);
		s.modify(1,1,n,pos,a[pos]);
		return R*work(l,r-1,L,min(R+m-1,r-l))%mod;
	}
}
void solve(int cs){
	cin>>n>>m;
	for(int i=1;i<=n;i++){
		cin>>a[i];
		p[a[i]]=i;
	}
	s.build(1,1,n);
	cout<<work(1,n,0,0)<<'\n';
}
signed main(){
	ios::sync_with_stdio(0);
	cin.tie(0);cout.tie(0);
	cin>>T;
	init();
	for(int cs=1;cs<=T;cs++){
		solve(cs);
	}
	return 0;
}
posted @ 2025-03-31 20:08  zxh923  阅读(34)  评论(0)    收藏  举报