卧薪尝胆,厚积薄发。
Charlie的云笔记序列
Date: Thu Oct 18 19:18:26 CST 2018
In Category:
NoCategory
Description:
给一个长为
$n$
的数列,设
$f[l][r]$
表示子串
$a[l\dots r]$
含有的本质不同子序列个数,求:
$$
\sum_{i=1}^n\sum_{j=i}^nf[i][j]
$$
$1\leqslant n\leqslant 10^5$
Solution:
设
$dp[i]$
表示
$\begin{align}\sum_{k=1}^if[k][i]\end{align}$
,那么转移为
$dp[i]=dp[i-1]\times2+2-(dp[last[a[i]]-1] + 1)$
。
乘二的含义是原来每个都可以在最后加当前这个字符或者不加,
$+2$
代表
$f[i][i]$
,但是这样会算重,因为在上一个这个数出现的位置有一些子序列已经被计算过了,所以要减掉,还有一个
$+1$
代表当前这一位之前被计算过一次,这次重复了一遍又加了一个,但是没有在上一个容斥中删掉,所以要再减一。
感觉还是不是很懂啊。
Code:
#include<algorithm>
#include<iostream>
#include<cstdlib>
#include<cstdio>
#include<cmath>
#include<map>
#include<cctype>
#include<cstring>
using namespace std;
int n;
#define MAXN 100010
int a[MAXN];
int f[MAXN];
map<int,int> last;
#define MOD 998244353
int main()
{
scanf("%d",&n);
for(int i = 1;i <= n;++i)scanf("%d",&a[i]);
for(int i = 1;i <= n;++i)
{
f[i] = (f[i - 1] * 2 % MOD + 2 - (last.find(a[i]) != last.end() ? f[last[a[i]] - 1] + 1 : 0) + MOD) % MOD;
last[a[i]] = i;
}
int ans = 0;
for(int i = 1;i <= n;++i)
{
ans = (ans + f[i]) % MOD;
}
cout << ans << endl;
return 0;
}
In tag:
DP-计数DP
Copyright © 2020
wjh15101051
ღゝ◡╹)ノ♡