CF1175F The Number of Subpermutations(单调栈,ST表)

给出一个数组\(a\),若子串\(a[l,r]\)满足\([1,r-l+1]\)各出现一次,则称其为这个数组的一个子排列。

求这个数组的子排列个数。

做法:

观察到一个子区间\([l,r]\)是一个排列,等价于:

\([l,r]\)没有重复的数字。

\([l,r]\)中的最大值为\(r-l+1\)

用单调栈处理出每个数作为最大值的左右端点。

那么以这个数为最大值的区间长度是固定的,且这个区间得覆盖这个数这个位置。

可以证明,这类区间的数量取决于这个数左右区间的较短的那个,所以暴力枚举覆盖这个数并以这个数为最大值的区间即可。

然后,如何判断一个区间没有重复的数字?

问题转化为这个区间内是否有一个数字出现了两次。

对每个数维护一个前驱出现位置,对这个区间询问前驱出现位置的最小值即可。

如果最小值>=l,说明有数字出现了两次。

查询最小值上ST表即可。

时间复杂度\(O(nlogn)\)

#include<bits/stdc++.h>
using namespace std;
const int maxn=3e5+10;
int n,a[maxn];
stack<int> st;
int L[maxn],R[maxn],pre[maxn],mp[maxn];
int dp[maxn][20],mm[maxn];
void initRMQ (int n) {
	mm[0]=-1;
	for (int i=1;i<=n;i++) {
		mm[i]=((i&(i-1))==0)?mm[i-1]+1:mm[i-1];
		dp[i][0]=pre[i];
	}
	for (int j=1;j<=mm[n];j++) {
		for (int i=1;i+(1<<j)-1<=n;i++) {
			dp[i][j]=max(dp[i][j-1],dp[i+(1<<j-1)][j-1]);
		}
	}
}
int rmq (int x,int y) {
	int k=mm[y-x+1];
	return max(dp[x][k],dp[y-(1<<k)+1][k]);
}
int main () {
	scanf("%d",&n);
	for (int i=1;i<=n;i++) scanf("%d",a+i);
	for (int i=1;i<=n;i++) L[i]=0,R[i]=n+1;
	for (int i=1;i<=n;i++) {
		while (st.size()) {
			int u=st.top();
			if (a[u]>=a[i]) {
				L[i]=u;
				break;
			}
			st.pop();
		}
		st.push(i);
	}
	while (st.size()) st.pop();
	for (int i=n;i>=1;i--) {
		while (st.size()) {
			int u=st.top();
			if (a[u]>a[i]) {
				R[i]=u;
				break;
			}
			st.pop();
		}
		st.push(i);
	}
	for (int i=1;i<=n;i++) {
		if (!mp[a[i]]) {
			mp[a[i]]=i;
			pre[i]=0;
		}
		else {
			pre[i]=mp[a[i]];
			mp[a[i]]=i;
		}
	}
	initRMQ(n);
	int ans=0;
	for (int i=1;i<=n;i++) {
	//	printf("%d %d %d\n",L[i]+1,R[i]-1,a[i]);
		int x=i-L[i];
		int y=R[i]-i;
		int len=a[i];
		if (x<y) {
			for (int j=L[i]+1;j<=i;j++) {
				if (j+len-1<i||j+len-1>=R[i]) continue;
				int l=j,r=j+len-1;
				if (rmq(l,r)<l) {
				//	printf("%d %d\n",l,r);
					ans++;
				}
			}
		}
		else {
			for (int j=i;j<=R[i]-1;j++) {
				if (j-len+1<=L[i]||j-len+1>i) continue;
				int l=j-len+1,r=j;
				if (rmq(l,r)<l) {
				//	printf("%d %d\n",l,r);
					ans++;
				}
			}
		}
	}
	printf("%d\n",ans);
}
原文地址:https://www.cnblogs.com/zhanglichen/p/15524788.html