codeforces Edu65 F. Scalar Queries(树状数组/线段树维护区间贡献)

F. Scalar Queries
题解:
看了很久才看懂的一道题,首先不难化简到 f(l,r)为a[i]*他在[l,r]中的区间第几小

**面对这种从区间到点的问题,如果很难考虑,应转化为从点转换到区间,考虑每个点的贡献,**首先,每个点的贡献是来自于其左边和右边某个比它小的数aj,

ai与aj,同时包含他们的每个区间会使a[i]*sum[i]中的sum[i]+1,这样就成功把区间排序改变,变成统计该数左边和右边每个比他小的数所贡献的所有区间总数,引用一下他人的话就是,

所以我们可以用树状数组+离散化来统计,树状数组下标是维护的是排序序号,维护小于该序号的数字的区间总数前缀和,但是要注意要加上本身的区间,因为我们统计的只是小于,本身要+1,如果没有小于他的数,他的序号也是1,还是要+1,这里利用树状数组在时间上的插入保证了统计的一定是在a[i]前面的数 树状数组和线段树的这个性质常考

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
#include<stack>
#include<vector>
#include<cmath>
#include<map>
#define ll long long
 
using namespace std;
const ll mod=1e9+7;
const int maxn=5e5+10;
map<int,int>mp;
ll c[maxn],sum[maxn];
int n,a[maxn],b[maxn];
void add(int x,int y)
{
   for(;x<=n;x+=x&-x)c[x]+=y; 
}
ll ask(int x)
{
   ll ans=0;
   for(;x;x-=x&-x)ans+=c[x];
   return ans;
}
int main()
{ 
    scanf("%d",&n);
    for(int i=1;i<=n;++i)
    {
       scanf("%d",&a[i]);
       b[i]=a[i]; 
    }
    sort(b+1,b+1+n);//离散化
    for(int i=1;i<=n;++i)
        mp[b[i]]=i;
    for(int i=1;i<=n;++i)
    {
       sum[i]=ask(mp[a[i]])*(n-i+1)%mod;    //前面比他小的
       add(mp[a[i]],i);     //左边区间个数 
    }
    for(int i=1;i<=n;++i)
        c[i]=0;
    for(int i=n;i>=1;--i)
    {
       sum[i]=(sum[i]+ask(mp[a[i]])*i%mod)%mod;   //右边比他小
       add(mp[a[i]],n-i+1);
       sum[i]=(sum[i]+(ll)(n-i+1)*i%mod)%mod;//还要统计自己d的
    }
    ll ans=0;
    for(int i=1;i<=n;++i)
        ans=(ans+(ll)a[i]*sum[i]%mod)%mod;
    printf("%lld\n",ans);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值