51nod 1680区间求和 (dp+树状数组/线段树)

不妨考虑已知一个区间[l,r]的k=1、k=2....k=r-l+1这些数的答案ans(只是这一个区间,不包含子区间)

那么如果加入一个新的数字a[i](i = r+1)

则新区间[l, i]的答案为ans + (c+1)*a[i] + s ,c为[l,r]中小于等于a[i]的数的个数,s为大于它的树的和

 

接下来考虑一个区间组,区间组i表示的是以i为结尾的所有区间

另dp[i]表示[1,i], [2,i] .... [i-1, i],[i, i]这些区间的答案和

那么dp[i+1] = dp[i] + ∑(j*a[j])(if a[j] >= a[i]) + ∑k*a[i](if a[k] < a[i]) + i*a[i]

这样的话,最后只需要把dp[1~n]加起来就构成了答案

上面的值可以使用树状数组或者线段树维护

这里还使用了hashmap来进行离散化

(线段树日常卡常数,优化很久才过)

 

#include <iostream>
#include <cstdio>
#include <cstring>
#include <vector>
#include <algorithm>
#include <ext/hash_map>
#define pb push_back
#define fi first
#define se second
#define mp make_pair
using namespace std;
using namespace __gnu_cxx;
const int maxn = 1e6 + 100;
typedef long long LL;
typedef pair<int, int> PII;
vector<int> V(maxn, 0);
const int MOD = (1e9 + 7) + 0.5;
hash_map <int, int> H;
PII tree[maxn*4];
PII v;
int k, L, R;
inline PII Add(PII a, PII b){
    return {(a.fi + b.fi)%MOD, (a.se + b.se)%MOD};
}
void Insert(int o, int l, int r){
    if(l == r){
        tree[o] = Add(tree[o], {v.fi, (LL)v.fi*v.se%MOD});
        return;
    }
    int mid = (l+r)>>1;
    if(k <= mid) Insert(o<<1, l, mid);
    else Insert((o<<1)+1, mid+1, r);
    tree[o] = Add(tree[o<<1], tree[(o<<1)+1]);
}
PII Query(int o, int l, int r){
    if(tree[o].fi == 0) return mp(0, 0);
    if(L <= l && r <= R){
        return tree[o];
    }
    int mid = (l+r)>>1;
    return Add((L <= mid ? Query(o*2, l, mid) : mp(0, 0)),
               (R > mid ? Query(o*2+1, mid+1, r) : mp(0, 0)));
}
int a[maxn], A, B, C;
int n;

void I_AM_ANGRY(){
    for(int i = 2; i <= n; i++){
        a[i] = ((LL)a[i-1]*A + B)%C;
        V[i] = a[i];
    }
    /*
    for(int i = 2; i <= n; i += 2){
        a[i] = ((LL)a[i-1]*A + B)%C;
        a[i+1] = ((LL)a[i]*A + B)%C;
        V[i] = a[i];
        V[i+1] = a[i+1];
    }
    */
    /*
    for(int i = 2; i <= n; i += 4){
        a[i] = ((LL)a[i-1]*A + B)%C;
        a[i+1] = ((LL)a[i]*A + B)%C;
        a[i+2] = ((LL)a[i+1]*A + B)%C;
        a[i+3] = ((LL)a[i+2]*A + B)%C;

        V[i] = a[i];
        V[i+1] = a[i+1];
        V[i+2] = a[i+2];
        V[i+3] = a[i+3];
    }*/
    V[1] = a[1];
}

int main()
{
    cin>>n>>a[1]>>A>>B>>C;
    I_AM_ANGRY();
    sort(V.begin(), V.end());
    int tot = 0;
    for(auto x : V) if(!H[x]) H[x] = ++tot;
    LL dp = a[1], ans = dp;
    int N = n; n = tot;
    v = {1, a[1]}; k = H[a[1]];
    Insert(1, 1, n);
    for(int i = 2; i <= N; i++){
        int t = H[a[i]];
        L = t; R = n;
        PII x = Query(1, 1, n);
        dp = (dp + x.se + (LL)(tree[1].fi - x.fi+i)*a[i])%MOD;
        v = {i, a[i]}; k = t;
        Insert(1, 1, n);
        ans += dp;
    }
    cout<<(ans%MOD + MOD)%MOD<<endl;
    return 0;
}

 

转载于:https://www.cnblogs.com/Saurus/p/6868376.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值