[CF1167F] Scalar Queries

题目链接

洛谷链接

Codeforces 链接

题目描述

给定一个序列\(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\) 的子串有:

\[\left\{ \begin{aligned} j(n-i+1)&,j<i\\ i(n-j+1)&,j>i\\ i(n-i+1)&,j=i \end{aligned} \right. \]

个,那么,我们便可以得到 \(sum_i\) 的公式:

\[\begin{aligned} sum_i&=\sum_{j<i\land a_j<a_i}j(n-i+1)+\sum_{j>i\land a_j<a_i}i(n-j+1)+i(n-i+1)\\ &=(n-i+1)\sum_{j<i\land a_j<a_i}j+i\sum_{j>i\land a_j<a_i}(n-j+1)+i(n-i+1) \end{aligned} \]

\(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;
}
posted @ 2025-07-31 19:51  Lijiangjun4  阅读(6)  评论(0)    收藏  举报