【ARC125D】Unique Subsequence
题目
题目链接:https://atcoder.jp/contests/arc125/tasks/arc125_d
给定一个长度为 \(n\) 的序列 \(a\)。求有多少个 \(a\) 的子序列 \(b\),满足在 \(a\) 的子序列可重集中,\(b\) 恰好出现了一次。
\(n\leq 2\times 10^5\)。
思路
假设一个长度为 \(m\) 的子序列 \(b\) 在 \(a\) 的一个前缀 \(a[1\sim n']\) 中恰好出现了一次(\(n'\) 就是 \(b_m\) 的下标),考虑在这个子序列后面加入下标为 \(i\) 的元素 \(a_i\) (\(i>n'\))。
手玩一下发现,只需要满足在 \((n',i)\) 这个区间中,不存在数字 \(b_m\) 和 \(a_i\) 即可。
设 \(f[i]\) 表示 \(a\) 中前 \(i\) 个元素有多少个子序列只出现了一次(且 \(i\) 强制选上),那么如果 \(f[i]\) 可以转移到 \(f[j]\),当且仅当 \((i,j)\) 中没有 \(a_i\) 和 \(a_j\)。
维护一个树状数组,当我们求完 \(f_i\) 后,把 \(f_i\) 插入树桩数组,当到达下一个为 \(a_i\) 的位置时,把 \(f[i]\) 从树状数组中删除。然后对于 \(j\),\(f[j]\) 只需要在树状数组中求一下区间 \([pre[j],j]\) 的和即可。
时间复杂度 \(O(n\log n)\)。
代码
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=200010,MOD=998244353;
int n,a[N],nxt[N],pre[N],last[N];
ll ans,f[N];
struct BIT
{
ll c[N];
void add(int x,ll v)
{
for (int i=x;i<=n+1;i+=i&-i)
c[i]=(c[i]+v)%MOD;
}
ll query(int x)
{
ll ans=0;
for (int i=x;i;i-=i&-i)
ans=(ans+c[i])%MOD;
return ans;
}
}bit;
int main()
{
scanf("%d",&n);
for (int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
if (last[a[i]])
nxt[last[a[i]]]=i,pre[i]=last[a[i]];
last[a[i]]=i;
}
bit.add(1,1);
for (int i=1;i<=n;i++)
{
f[i]=(bit.query(i)-bit.query(pre[i]))%MOD;
if (pre[i]) bit.add(pre[i]+1,-f[pre[i]]);
if (!nxt[i]) ans=(ans+f[i])%MOD;
bit.add(i+1,f[i]);
}
cout<<(ans%MOD+MOD)%MOD;
return 0;
}