[CF1167F] Scalar Queries
题目链接
题目描述
给定一个序列\(a\),\(a\)中的元素两两不同。
定义\(f(l,r)\)为:
-
对于 \(1\le i\le r-l+1\),记 \(b_i=a_{l-1+i}\),将\(b\)排成升序。
-
\(f(l,r)=\sum_{i=1}^{r-l+1} {b_i \cdot i}\)
求 \(\sum_{1\le l\le r\le n} f(l,r)\),对\(10^9+7\)取模。
说明/提示
对于30%的数据,\(n \le 100\)。
对于60%的数据,\(n \le 2000\)。
对于100%的数据,\(1 \le n \le 5\times 10^{5},1 \le a_{i} \le 10^{9}\)。
解题思路
题面中译中,就是:对于每个子串,它的贡献是子串内的每个数乘排名的积之和,求所有子串的贡献之和。
像这种风格的题,肯定是要拆位,计算每个数对答案的贡献。设 \(a_i\) 对答案的贡献为 \(a_i\times sum_i\),那么 \(sum_i\) 就是 \(a_i\) 在不同子串里的排名之和。
考虑 \(a_i\) 在子串 \([l,r]\) 中的贡献,则 \([l,r]\) 中每个小于等于 \(a_i\) 的数都会使 \(a_i\) 的排名加 \(1\)(包括 \(a_i\),因为排名至少为 \(1\)),也就是说,对于 \(a_j\leq a_i\),所有同时包含 \(a_i\) 和 \(a_j\) 的子串都会对 \(sum_i\) 产生 \(1\) 的贡献。
同时包含 \(a_i\) 和 \(a_j\) 的子串有:
个,那么,我们便可以得到 \(sum_i\) 的公式:
记 \(L_i=\displaystyle\sum_{j<i\land a_j<a_i}j\),\(R_i=\displaystyle\sum_{j>i\land a_j<a_i}(n-j+1)\),则 \(sum_i=(n-i+1)L_i+iR_i+i(n-i+1)\),而 \(L_i\) 和 \(R_i\) 可以树状数组维护,时间复杂度为 \(O(n\log n)\),可以通过本题
参考代码
#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#define int long long
#define mod 1000000007
#define N 500005
using namespace std;
int n,cnt,a[N],b[N],tmp[N];
int t[N],l[N],r[N];
long long ans;
int lowbit(int x)
{
return x&(-x);
}
void modify(int x,int c)
{
while(x<=n)
{
t[x]+=c;
x+=lowbit(x);
}
return;
}
int getsum(int x)
{
int res=0;
while(x)
{
res+=t[x];
x-=lowbit(x);
}
return res;
}
signed main()
{
scanf("%lld",&n);
for(int i=1;i<=n;i++) scanf("%lld",&a[i]),tmp[i]=a[i];
sort(tmp+1,tmp+n+1);
cnt=unique(tmp+1,tmp+n+1)-(tmp+1);
for(int i=1;i<=n;i++) b[i]=lower_bound(tmp+1,tmp+cnt+1,a[i])-tmp;
for(int i=1;i<=n;i++)
{
l[i]=getsum(b[i]);
modify(b[i],i);
}
memset(t,0,sizeof(t));
for(int i=n;i>=1;i--)
{
r[i]=getsum(b[i]);
modify(b[i],n-i+1);
}
for(int i=1;i<=n;i++)
{
ans=(ans+a[i]*(l[i]*(n-i+1)%mod+r[i]*i%mod+i*(n-i+1)%mod)%mod)%mod;
}
printf("%lld\n",ans);
return 0;
}