题目大意
n ( 1 ≤ n ≤ 5 × 1 0 5 ) n(1\le n\le 5\times 10^5) n(1≤n≤5×105) 颗带颜色的珠子,从左到右选出若干颗珠子(不少于 1 1 1 颗),问有多少种方案。答案模 m m m.
题解
小 dp 题。
首先对珠子颜色离散化,记得开 long long
. 不多说。
观察发现,选出珠子不考虑原数组中的位置,只需要保证珠子序列在原数组中是从左到右的。
故设状态 f [ i ] f[i] f[i] 表示以 a [ i ] a[i] a[i] 结尾的珠子序列的方案数。
从左到右枚举,考虑第 i i i 颗珠子(颜色为 a [ i ] a[i] a[i])能接在哪些序列后。记离第 i i i 颗珠子最近的颜色与这颗珠子相同的珠子位置为 l s t [ i ] lst[i] lst[i]. 那么有
f [ i ] = ∑ j = l s t [ i ] i − 1 f [ j ] f[i]=\sum\limits_{j=lst[i]}^{i-1}f[j] f[i]=j=lst[i]∑i−1f[j]
为什么?
观察序列:1,2,3,2,3,2
最后一颗珠子颜色为 2 2 2,它不能接在第 1 1 1 到 3 3 3 颗珠子后,因为这种情况(在第 1 1 1 到 3 3 3 颗珠子后接上颜色为 2 2 2 的珠子)已经被第 4 4 4 颗珠子统计过了。
O ( n log n ) O(n\log n) O(nlogn) 离散化, O ( n ) O(n) O(n) 预处理出 l s t lst lst 数组,前缀和优化 dp O ( n ) O(n) O(n),总的时间复杂度 O ( n log n ) O(n\log n) O(nlogn).
代码
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 500005;
int n, m, c[N], lst[N];
LL f[N], b[N], a[N], ans = 0, sum[N];
int main() {
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; i++) scanf("%lld", &a[i]), b[i] = a[i];
sort(b + 1, b + n + 1);
for (int i = 1; i <= n; i++) a[i] = lower_bound(b + 1, b + n + 1, a[i]) - b;
for (int i = 1; i <= n; i++) lst[i] = c[a[i]], c[a[i]] = i;
for (int i = 1; i <= n; i++) {
if (lst[i]) f[i] = (sum[i - 1] - sum[lst[i] - 1] + m) % m;
else f[i] = (sum[i - 1] + 1) % m;
sum[i] = (sum[i - 1] + f[i]) % m;
}
for (int i = 1; i <= n; i++) ans = (ans + f[i]) % m;
printf("%lld", ans);
return 0;
}
END
第 30 30 30 篇博客!