AT_abc017_D サプリメント

题目传送门

题意

对于一串由数字构成的序列,将其分割成若干段,且每一段内的每个数字最多只有一个。求其方案数对 \(10^9+7\) 取模后的结果。

思路

考虑使用 DP 进行求解。设 \(dp_i\) 为只使用前 \(i\) 个数字的方案数。那么怎么求呢?我们发现只要从左端点 \(l \sim i\) 内没有重复的数字,就可以加上 \(dp_{last-1}\) 的方案数,每次循环都向前搜索的暴力时间复杂度就是 \(O(n^2)\)。

优化

简单观察一下就会发现,左端点 \(l\) 是单调递增且易求的。那我们就可以考虑用一个变量 \(last\) 来存储左端点。每次循环都用 \(f\) 数组记录一下 \(last \sim i\) 这段区间内是否有重复的数字,如果有就使 \(last\) 加一。最后我们再用前缀和数组 \(sum\) 来优化,就可以让整体时间复杂度仅为 \(O(n)\)。


初始化

dp[0]=dp[1]=sum[0]=f[a[1]]=1,sum[1]=2

AC code

#include <bits/stdc++.h>
using namespace std;
#pragma G++ optimize(2,3,"Ofast","inline")
#define int long long
#define double long double
#define endl '\n'
const int N=114514,mod=1e9+7;
int n,m,ans,cnt,sum[N]={1,2},last=1,a[N],dp[N]={1,1},f[N];
signed main(){
	ios::sync_with_stdio(false);
	cin.tie(0);
	cout.tie(0);
	cin >> n >> m;
	for(int i = 1;i<=n;i++) cin >> a[i];
	f[a[1]]++;
	for(int i = 2;i<=n;i++){
		cnt+=(++f[a[i]]==2);
		while(cnt&&last<=i) cnt-=(--f[a[last++]]==1);//尺取
		dp[i]=sum[i-1]-sum[last-2];dp[i]=(dp[i]%mod+mod)%mod;//每一步都取模防止爆 long long
		sum[i]=sum[i-1]+dp[i];sum[i]=(sum[i]%mod+mod)%mod;//每一步都取模防止爆 long long
	}
	cout << dp[n] << endl;
	return 0;
}
posted @ 2026-10-22 07:30  elon_giao  阅读(4)  评论(0)    收藏  举报