题目大意
给定一个长度为
n
n
n 的序列
a
a
a,求:
∑
1
≤
l
<
r
≤
n
∑
l
≤
x
<
y
≤
r
[
a
x
=
a
y
]
\sum_{1\le l<r\le n}\sum_{l\le x<y\le r}[a_x=a_y]
1≤l<r≤n∑l≤x<y≤r∑[ax=ay]
即:求每一个连续区间内的相等数对之和。
Solution
套路题,如果直接求不好求,那么算每一部分的贡献。
首先考虑如果两个数 a i = a j a_i=a_j ai=aj( i < j i<j i<j),那么其产生的贡献是 i × ( n − j + 1 ) i\times (n-j+1) i×(n−j+1),也就是有 i × ( n − j + 1 ) i\times (n-j+1) i×(n−j+1) 个区间包含它。
此时考虑如果有第三个
a
k
=
a
i
=
a
j
a_k=a_i=a_j
ak=ai=aj(
k
>
j
k>j
k>j),这个时候贡献会加上:
i
×
(
n
−
k
+
1
)
+
j
×
(
n
−
k
+
1
)
i\times (n-k+1)+j\times (n-k+1)
i×(n−k+1)+j×(n−k+1)
=
(
i
+
j
)
×
(
n
−
k
+
1
)
=(i+j)\times (n-k+1)
=(i+j)×(n−k+1)
于是我们想到,对于每一个值,我们记录这个值的下标的和为
s
u
m
sum
sum,当这个值新加入了一个下标时,我用这个和来更新答案:
a
n
s
=
a
n
s
+
s
u
m
×
(
n
−
i
+
1
)
ans=ans+sum\times (n-i+1)
ans=ans+sum×(n−i+1)
其中
i
i
i 为新加入的下标。
那么问题是值域比较大,所以用到离散化即可。
Code
#include<bits/stdc++.h>
#define inf 1<<30
#define INF 1ll<<60
#define ll long long
using namespace std;
const int MAXN=1e5+10;
int a[MAXN],b[MAXN];
ll sum[MAXN];
void solve(){
int n;
scanf("%d",&n);
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
b[i]=a[i];
}
sort(b+1,b+1+n);
int cnt=unique(b+1,b+1+n)-b-1;
for(int i=1;i<=n;i++) a[i]=lower_bound(b+1,b+1+cnt,a[i])-b;
//离散化
ll ans=0;
for(int i=1;i<=n;i++){
if(!sum[a[i]]) sum[a[i]]+=i;
else{
ans+=sum[a[i]]*(n-i+1);
sum[a[i]]+=i;
}
}printf("%lld\n",ans);
for(int i=1;i<=cnt;i++) sum[i]=0;//注意不要用 memset 全部更新,这题能卡掉(亲测)
}
int main()
{
int T;
for(scanf("%d",&T);T--;)
solve();
}