题解 P6477 【[NOI Online 2 提高组]子序列问题sequence(暂无数据,禁止提交)】
考场上最后一分钟写完了正解,结果提交的一瞬间刚好结束。。。
这里就来分享一下考场上的思路吧。
题解:
首先,对于区间去重后的元素个数,有一种套路化的递推方法:
-
设
\operatorname{pre}(i) 表示a_i 上一次出现的位置 -
则
f(l,i)=\begin{cases}1\quad\quad\quad\quad\quad\quad\;\;\; l=i\\f(l,i-1)\quad\quad\quad l\leq\operatorname{pre}(i)\\f(l,i-1)+1\quad \text{else}\end{cases}
如何求出
由于不好解释,直接放代码了
for(int i=1;i<=n;i++)
cin>>a[i],b[i]=a[i];
sort(b+1,b+n+1);
cnt=unique(b+1,b+n+1)-b-1;
for(int i=1;i<=n;i++){
int pos=lower_bound(b+1,b+cnt+1,a[i])-b;
a[i]=pos;
} //以上为离散化
for(int i=1;i<=n;i++){
if(pree[a[i]])pre[i]=pree[a[i]]; //pree 数组指向 a[i] 上次出现的位置
pree[a[i]]=i;
}
然后这道题
我们需要求
函数的前缀和,这部分可以直接扫描
直接把
其中
计算
当
具体地说,
-
给
S_i(\operatorname{pre}(i)+1) 加上1 ,给S_i(\operatorname{pre}(i)+2) 加上2 ,……,给S_i(i-1) 加上i-\operatorname{pre}(i)-1 ; -
给
S_i(i) 赋值S_i(i-1)+1 。
所以,我们需要写一个数据结构,支持
-
区间加等差数列
-
单点修改
-
前缀和查询
显然树状数组,线段树都可以做到。这里我写的树状数组,常数较小。
树状数组在处理区间加等差数列时,只能对原数列差分,将区间加等差数列变为区间加。
树状数组处理区间加,常规方法是对原数列再差分一次。
也就是说,我们需要用树状数组维护
-
区间加
-
前缀和查询
这是可以做到的当时线段树1我就这么做的:
设待处理数列为
A ,其差分数列为B_i=A_i-A_{i-1} 。则
\sum_{i=1}^{n}A_i=(n+1)\sum_{i=1}^{n}B_i-\sum_{i=1}^{n}iB_i 维护数列
B 和数列iB 的前缀和即可。
也就是说,我们可以在
所以总时间复杂度
但是常数较大,需注意优化。
这道题难度还是比较大的(可能是我太弱了),个人意见:难度评级 蓝~紫