51nod1680 区间求和

有n个数,给定一个k,求所有长度大于等于k的区间中前k大数的总和。这样就比较简单相信大家都会,所以此题要求当k=1~n的总和,即求

 nk=1nk+1i=1nj=i+k1  区间前K大和
Input
输入五个数n,a1,A,B,C。a1表示第一个数,A,B,C用来生成其余n-1个数。a(i)=(a(i-1)*A+B)mod C。1<=n<=1,000,000,0<=a1,A,B,C<=1,000,000,000
Output
一个数表示答案,最后答案对1,000,000,007取模。
Input示例
3 3 1 1 10
Output示例63样例解释:
三个数为3,4,5
K=1:[1,1]=3,[1,2]=[2,2]=4,[1,3]=[2,3]=[3,3]=5(表示各个区间在k=1时的答案)
K=2:[1,2]=7,[2,3]=[1,3]=9
K=3:[1,3]=12

题目大意:求sigma(k=1--n) f(k),k为区间大于等于k的前k大的和。

题解:树状数组
对于区间[l,r],假设它的答案为ans,如果新增一个数a[r+1],那么它
出现的次数就是[l,r]中小于它的数的个数。如果Ai<Aj,i<j,那么包含这两个数
的区间的个数为i*(n-j+1),则对答案的贡献为Aj*(n-j+1),对于Aj>Ai,i>j,
倒过来再处理一遍。注意:双关键字排序

代码:

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#define mod 1000000007
#define maxn 1000009
#define LL long long
using namespace std;

LL n,ans,A,B,C,a[maxn],tree[maxn];
typedef pair<LL,int> PII;
PII b[maxn];

void add(int x,int p){
    for(;x<=n;x+=x&(-x))tree[x]=(tree[x]+p)%mod;
}

LL getsum(int x){
    LL all=0;
    for(;x;x-=x&(-x))all=(all+tree[x])%mod;
    return all;
}

int main(){
    scanf("%lld%lld%lld%lld%lld",&n,&a[1],&A,&B,&C);
    b[1].first=a[1];b[1].second=1;
    for(int i=2;i<=n;i++){
        a[i]=(a[i-1]*A+B)%C;
        b[i].first=a[i];b[i].second=i;
    }    
    sort(b+1,b+n+1);
    for(int i=1;i<=n;i++)
     a[i]=lower_bound(b+1,b+n+1,make_pair(a[i],i))-b;
    for(int i=1;i<=n;i++){
        add(a[i],i);
        LL tmp=b[a[i]].first*1LL*(n-i+1)%mod;
        LL amp=getsum(a[i]);
        ans=(ans%mod+tmp*amp%mod)%mod;
    }
    memset(tree,0,sizeof(tree));
    for(int i=n;i>=1;i--){
        LL  tmp=(b[a[i]].first*1LL*i)%mod;
        LL  amp=getsum(a[i]);    
        ans=(ans%mod+tmp*amp%mod)%mod;
        add(a[i],n-i+1);
    }
    printf("%lld
",ans);
    return 0;
}
 
原文地址:https://www.cnblogs.com/zzyh/p/7650422.html