CF368B Sereja and Suffixes 题解
Content
有 \(n\) 个数 \(a_1,a_2,a_3,...,a_n\)。有 \(m\) 次询问,每次给定一个数 \(l\),查询在区间 \([l,n]\) 内不同的数一共有多少个。
数据范围:\(1\leqslant n,m\leqslant 10^5,1\leqslant a_i\leqslant 10^5\)。
Solution
鉴于 \(\mathcal{O}(nm)\) 的暴力模拟显然不能够通过此题,我们需要一种更高效的算法,以卡过这样的数据。
那么,我们自然而然就会想到 \(\texttt{dp}\) 的算法了。
既然是要求 \([l,n]\) 里面的不同的数的个数,那么我们不妨就设 \(dp_i\) 为 \([i,n]\) 里面不同的数的个数。那么,我们显然可以先推出这样一个式子:
很显然,\([n,n]\) 里面不同的数只有 \(a_n\) 一个,对吧?那么我们就从这个临界点开始从右往左进行 \(\texttt{dp}\)。
为了统计这个数字是否出现,我们需要开一个 \(vis\) 数组(这里 \(a_i\) 的值不算太大,如果很大的时候就需要用到 \(\texttt{map}\) 来实现了)。那么,这个题目的转化方程就很容易想到了:如果在 \(i\) 之后出现了一个数 \(j\),使得 \(a_j=a_i\),那么 \(dp_i\) 就保持和 \(dp_{i+1}\) 不变,否则,又出现了一个不同的数,那么 \(dp_i\) 相比较于 \(dp_{i+1}\) 就会多 \(1\)。
这样,转移方程就列完了:
我们拿样例 \(1\) 做个例子:
1 2 3 4 1 2 3 4 100000 99999
显然,\(dp_{10}=1\)。
更新 \(vis_{99999}=1\),从 \(10\) 这个位置开始从右往左扫。
-
扫到 \(9\) 这个位置,显然,后面并没有出现 \(100000\) 这个数,所以 \(dp_9=dp_{10}+1=2\),并更新 \(vis_{100000}=1\)。
-
扫到 \(8\) 这个位置,显然,后面并没有出现 \(4\) 这个数,所以 \(dp_8=dp_9+1=3\),并更新 \(vis_4=1\)。
-
扫到 \(7\) 这个位置,显然,后面并没有出现 \(3\) 这个数,所以 \(dp_7=dp_8+1=4\),并更新 \(vis_3=1\)。
-
扫到 \(6\) 这个位置,显然,后面并没有出现 \(2\) 这个数,所以 \(dp_6=dp_7+1=5\),并更新 \(vis_2=1\)。
-
扫到 \(5\) 这个位置,显然,后面并没有出现 \(1\) 这个数,所以 \(dp_5=dp_6+1=6\),并更新 \(vis_1=1\)。
-
扫到 \(4\) 这个位置,显然,位置 \(8\) 出现了 \(4\) 这个数,有 \(vis_4=1\),所以 \(dp_4=dp_5=6\)。无须再更新 \(vis_4\) 了,下同。
-
扫到 \(3\) 这个位置,显然,位置 \(7\) 出现了 \(3\) 这个数,有 \(vis_3=1\),所以 \(dp_3=dp_4=6\)。
-
扫到 \(2\) 这个位置,显然,位置 \(6\) 出现了 \(2\) 这个数,有 \(vis_2=1\),所以 \(dp_2=dp_3=6\)。
-
扫到 \(1\) 这个位置,显然,位置 \(5\) 出现了 \(1\) 这个数,有 \(vis_1=1\),所以 \(dp_1=dp_2=6\)。
这样,所有的 \(dp\) 数组全部更新完了:
显然,这和样例所给出的答案完全相符。相信对这个 \(\texttt{dp}\) 的过程有了一定的加深理解。
那么,既然\(\mathcal{O}(n)\) 的 \(\texttt{dp}\) 预处理完了,剩下的询问就可以直接 \(\mathcal{O}(1)\) 轻松搞定了,这下就可以轻松通过此题了。
Code
本题只放 \(\texttt{dp}\) 过程的核心代码部分,其中:
- \(dp_i\) 表示 \([i,n]\) 内不同的数的个数。
- \(vis_i\) 表示 \(i\) 是否出现过,是的话 \(vis_i=1\),否则 \(vis_i=0\)。
f[n] = 1;
vis[a[n]] = 1;
for(int i = n - 1; i >= 1; --i) {
if(!vis[a[i]]) {
f[i] = f[i + 1] + 1;
vis[a[i]] = 1;
} else f[i] = f[i + 1];
}