2022.10.25 总结

B

有一个长度为 \(n\) 的排列,你可以进行若干操作,每次操作选择相邻的两个数并删去较大的数。
问最后可以生成多少不同的序列。

\(f_i\) 为以 \(i\) 为结尾的序列数。
\(f_i=\sum f_j\) , 仅当区间 \([i,j]\) 内所有数都大于 \(\min(a_i,a_j)\) 时。

设向前第一个 \(a_k<a_i\) 的下标为 \(k\).
则贡献取之与 \([k,i-1]\) 之间所有数 以及 \([1,k-1]\) 间满足上述条件的数。
我们用递增单调栈维护一下。

那么答案是什么呢,答案是从前往后枚举,当 \(a_i\) 是前缀中最小的数,\(f_i\) 的和。

code
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=1e5+10,mod=1e9+7;
int a[N],n,stk[N],tp,sum[N],val[N],f[N],res;
signed main() {
	cin>>n;
	for(int i=1; i<=n; i++) {
		cin>>a[i];
		while(a[i]<a[stk[tp]]&&tp) tp--;
		f[i]=(sum[i-1]-sum[stk[tp]]+val[tp])%mod;
		if(!tp) f[i]++;
		stk[++tp]=i;
		val[tp]=(val[tp-1]+f[i])%mod;
		sum[i]=sum[i-1]+f[i];
	}
	tp=0;
	for(int i=n; i>=1; i--) {
		while(a[stk[tp]]>a[i]) --tp;
		if(!tp) res=(res+f[i])%mod;
		stk[++tp]=i;
	}
	cout<<(res+mod)%mod;
	return 0;
}

C

维护一个计算序列,其中加号与乘号运算优先级一致。
每次更改一个符号或者一个数,问运算结果。
分块或线段树即可。

posted @ 2022-10-26 09:53  s1monG  阅读(22)  评论(0)    收藏  举报