专栏文章

题解:P14567 【MX-S12-T2】区间

P14567题解参与者 2已保存评论 1

文章操作

快速查看文章及其快照的属性,并进行相关操作。

当前评论
1 条
当前快照
1 份
快照标识符
@min2y3km
此快照首次捕获于
2025/12/01 19:42
3 个月前
此快照最后确认于
2025/12/01 19:42
3 个月前
查看原文
确定性的线性做法。
Lc,RcL_c,R_c 表示一个颜色 cc 最早出现和最晚出现的位置。
由于 ff 单调不降,如果存在 22 个合法区间 [l1,r1],[l2,r2][l_1,r_1],[l_2,r_2] 满足 [l2,r2][l1,r1][l_2,r_2]\subset[l_1,r_1],那就可以把 [l1,r1][l_1,r_1] 给扔掉。另外,容易证明如果存在 l1l2r1r2l_1\le l_2\le r_1\le r_2[l2,r1][l_2,r_1] 也是合法区间。
所以对于每个左端点 ll,求出最小的 rr 满足 [l,r][l,r] 合法,然后把可以扔掉的区间扔掉后就只剩下一些不交的区间,这时候再计算剩下所有区间的答案即可。
扔掉区间是容易线性的,考虑怎么给 ll 求出 rr。从右到左扫描 ll,一个显然的是 Lc<lL_c<lcc 不能出现在 [l,r][l,r] 中。由于是从右往左扫,可以用一个单调栈维护所有不合法的位置,然后这部分要求就相当于 r<topr<top。在满足这个后只要求 cLcR,RcR\forall c|L_c\le R,R_c\le R,可以把 RcR_c 看作 11LcL_c 看作 1-1,那只要 [l,r][l,r] 和为 00 即可,记录后缀和开桶维护。
CPP
#include <bits/stdc++.h>
#define ll long long
using namespace std;
#define getchar() (S==T&&(T=(S=B)+fread(B,1,1<<20,stdin),S==T)?EOF:*S++)
char B[1<<20],*S=B,*T=B;
inline int read(){
    int x=0,c=getchar();
    while(c<48) c=getchar();
    while(c>47) x=(x*10)+(c^48),c=getchar();
    return x;
}
int n,c[1000005],v[1000005],f[1000005],r[1000005],s[1000005],stk[1000005],top,lst[1000005],pre[1000005],fs[1000005],ls[1000005];
int main(){
	n=read();
	for(int i=1;i<=n;i++) c[i]=read(),fs[c[i]]=(fs[c[i]]?fs[c[i]]:i),ls[c[i]]=i;
	for(int i=1;i<=n;i++) v[i]=read();
	for(int i=1;i<=n;i++) f[i]=read();
	lst[0]=n+1;
	for(int i=n,s=0;i;i--){
		s+=(ls[c[i]]==i)-(fs[c[i]]==i),stk[++top]=i;
		while(top&&fs[c[stk[top]]]>=i) top--;
		if(lst[s]&&(!top||lst[s]<=stk[top])) r[i]=lst[s]-1;
		lst[s]=i,pre[r[i]]=i;
	}
	for(int i=2;i<=n;i++) pre[i]=max(pre[i-1],pre[i]);
	ll ans=1e18;
	for(int i=1;i<=n;i++)
		if(r[i]&&pre[r[i]]==i){
			ll res=0;
			for(int j=i;j<=r[i];j++) res+=v[j]*f[j-i+1];
			ans=min(ans,res);
		}
	cout<<ans<<'\n';
	return 0;
}

评论

1 条评论,欢迎与作者交流。

正在加载评论...