CF1175F The Number of Subpermutations 题解

Codeforces
Luogu

Description.

求一个序列中是排列的子串数量。

Solution.

首先,要满足是个排列,必须满足一下性质

  1. 最大值是 r-l+1
  2. 元素之间互不重复

我们考虑枚举最大值,那当前区间最长长度也知道了。
因为它既是区间最大值,又需要满足区间不重复,
所以我们只需要找出它前一个和后一个 \(\ge\) 它的,在那边停止。
同时因为区间长度已经给定,所以我们可以 \(O(n)\) 扫描。
复杂度至多 \(O(n^2)\)

然后我们发现,加一个优化后,复杂度就变成了 \(O(n\log n)\)
优化大概就是因为当前区间左右端点需要同时满足在左边和右边,
所以我们可以找出左右中扩展次数少一点的一侧,并枚举那一侧。
可以证明,这样复杂度是 \(O(n\log n)\),在最后会附上不严谨证明。

然后这题就做完了。
注意以上复杂度只是上界,实现时还需要保证序列内不存在相同的两个数。

Coding

点击查看傻逼写的代码
//是啊,你就是那只鬼了,所以被你碰到以后,就轮到我变成鬼了{{{
#include<bits/stdc++.h>
using namespace std;typedef long long ll;
template<typename T>inline void read(T &x)
{
	x=0;char c=getchar(),f=0;
	for(;c<48||c>57;c=getchar()) if(!(c^45)) f=1;
	for(;c>=48&&c<=57;c=getchar()) x=(x<<1)+(x<<3)+(c^48);
	f?x=-x:x;
}/*}}}*/
int n,a[300005],ls[300005],nx[300005],pr[300005],st[300005],tp;
int main()
{
	read(n);int rs=0;for(int i=1;i<=n;i++) read(a[i]);
	memset(st,0,sizeof(st));for(int i=1;i<=n;i++) pr[i]=st[a[i]],st[a[i]]=i;
	for(int i=1;i<=n;st[++tp]=i++) while(tp&&a[st[tp]]<=a[i]) nx[st[tp]]=i,tp--;
	while(tp) nx[st[tp]]=n+1,tp--;
	for(int i=n;i>=1;st[++tp]=i--) while(tp&&a[st[tp]]<=a[i]) ls[st[tp]]=i,tp--;
	while(tp) ls[st[tp]]=0,tp--;
	for(int i=1;i<=n;i++) pr[i]=max(pr[i-1],pr[i]);
	//for(int i=1;i<=n;i++) printf("<%d,%d>%c",ls[i],nx[i],i==n?'\n':' ');
	for(int i=1;i<=n;i++) if(i-ls[i]<nx[i]-i)
		{for(int j=ls[i]+1;j<=i;j++) if(j+a[i]-1>=i&&j+a[i]-1<nx[i]&&pr[j+a[i]-1]<j) rs++;}
	else {for(int j=i;j<nx[i];j++) if(j-a[i]+1>ls[i]&&j-a[i]+1<=i&&pr[j]<j-a[i]+1) rs++;}
	return printf("%d\n",rs),0;
}
原文地址:https://www.cnblogs.com/pealfrog/p/15017520.html