原题链接
简洁题意
求一个序列的任意一个子串(由连续的数组成)组成的集合大小之和(集合过滤重复元素)。
分析:
这个题可以思考,要想某一个数在一个字串中有贡献,当且仅当它是该集合第一个出现的数。所以,可以统计一下上一个出现的位置,
i
i
i到这个位置后面都能有贡献。用
d
d
d数组存每个
a
i
a_i
ai上一次出现的位置。因为
a
a
a有点大,数组存不下,可以考虑
m
a
p
map
map或者离散化。
现在,设
g
(
r
)
=
∑
l
=
1
r
f
(
l
,
r
)
2
g(r)=\displaystyle\sum_{l=1}^r f(l,r)^2
g(r)=l=1∑rf(l,r)2,则可以枚举每一个
r
r
r计算之间的变化。计算增加一个
a
r
a_r
ar会有什么变化,则计算
g
(
r
)
−
g
(
r
−
1
)
g(r)-g(r-1)
g(r)−g(r−1)即可。我们假定
f
(
l
,
r
)
=
0
,
l
>
r
f(l,r)=0,l>r
f(l,r)=0,l>r
=
∑
l
=
1
r
f
(
l
,
r
)
2
−
∑
l
=
1
r
−
1
f
(
l
,
r
−
1
)
2
=\sum_{l=1}^{r}f(l,r)^2-\sum_{l=1}^{r-1}f(l,r-1)^2
=l=1∑rf(l,r)2−l=1∑r−1f(l,r−1)2只有
l
a
s
t
r
+
1
last_r+1
lastr+1到
r
r
r的区间减下来是有值的,因此原式得
=
∑
l
=
l
a
s
t
r
+
1
r
(
f
(
l
,
r
)
2
−
f
(
l
,
r
−
1
)
2
)
=\sum_{l=last_r+1}^r (f(l,r)^2-f(l,r-1)^2)
=l=lastr+1∑r(f(l,r)2−f(l,r−1)2)因为两个平方底数的差一定是
1
1
1,则
=
∑
l
=
l
a
s
t
r
+
1
r
(
2
f
(
l
,
r
−
1
)
+
1
)
=\sum_{l=last_r+1}^r (2f(l,r-1)+1)
=l=lastr+1∑r(2f(l,r−1)+1)
=
∑
l
=
l
a
s
t
r
+
1
r
2
f
(
l
,
r
−
1
)
+
r
−
l
a
s
t
r
=\sum_{l=last_r+1}^r 2f(l,r-1)+r-last_r
=l=lastr+1∑r2f(l,r−1)+r−lastr我们每次只用加上这个值即可。
后面非求和的部分可以直接计算,那么前面
f
f
f求和的地方怎么算呢?可以利用差分,在
l
a
s
t
r
+
1
last_r+1
lastr+1加一,
i
+
1
i+1
i+1减一,这样这个区间的
f
f
f和就已经计算了,这样求一个前缀和即可。求前缀和用树状数组。因为我们
f
f
f求的是
f
(
l
,
r
−
1
)
f(l,r-1)
f(l,r−1),则要在求前缀和之后加上差分标记。最后,答案就是
∑
r
=
1
n
g
(
r
)
\displaystyle\sum_{r=1}^{n}g(r)
r=1∑ng(r)。
每次操作时间复杂度是
Θ
(
log
n
)
\Theta(\log n)
Θ(logn)的,总计是
Θ
(
n
log
n
)
\Theta(n\log n)
Θ(nlogn)。
代码:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int NN=1000005,P=1e9+7;
ll t1[NN],t2[NN];
int a[NN],b[NN],d[NN],last[NN];
ll lowbit(int x)
{
return x&-x;
}
void add(int x,int d,int n)
{
int y=x;
while(y<=n)
{
t1[y]+=d;
t2[y]+=1ll*d*x;
y+=lowbit(y);
}
}
ll sum(int x)
{
ll res=0;
int y=x;
while(y)
{
res+=t1[y]*(x+1)-t2[y];
y-=lowbit(y);
}
return res;
}
int main()
{
int n;
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
b[i-1]=a[i];
}
sort(b,b+n);
int m=unique(b,b+n)-b;
for(int i=1;i<=n;i++)
{
a[i]=lower_bound(b,b+m,a[i])-b;
last[i]=d[a[i]];
d[a[i]]=i;
}
ll ans=0,now=0;
for(int i=1;i<=n;i++)
{
(now+=i-last[i]+2*(sum(i)-sum(last[i])))%=P;
ans+=now;
add(last[i]+1,1,n);
add(i+1,-1,n);
}
printf("%lld",ans%P);
return 0;
}