Second Sum 【AtCoder - 5206】

vjudge链接VJ

原题链接(推荐)


Second Sum 【AtCoder - 5206】_第1张图片

  其实,他就是问了这么一个问题,问所有区间的第二大的数之和,那么我们其实很容易发现一个规律,就是每个数会对答案产生的贡献。

  每个数,在它的左边和右边,分别有第一个大于它的数,和第二个大于它的数,我们可以首先处理出来这个。然后看一下图例:

Second Sum 【AtCoder - 5206】_第2张图片

蓝色区间长度相乘加上红色区间长度相乘,所以不就是“本身”这个值产生的贡献的次数了吗?然后就可以计算出来了。

那么问题就转变成为怎么求左边的第一个比他大的,和第二个比他大的(右边同理),我们可以不断的向左二分区间最远到达长度,然后就可以求出来第一个大于它的值的所在的位子,然后再继续找下去,时间复杂度O(log * log),又因为要找N次,所以总的复杂度是O(N * logN * logN)。

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#define lowbit(x) ( x&(-x) )
#define pi 3.141592653589793
#define e 2.718281828459045
#define INF 0x3f3f3f3f
#define eps 1e-6
#define HalF (l + r)>>1
#define lsn rt<<1
#define rsn rt<<1|1
#define Lson lsn, l, mid
#define Rson rsn, mid+1, r
#define QL Lson, ql, qr
#define QR Rson, ql, qr
#define myself rt, l, r
using namespace std;
typedef unsigned long long ull;
typedef unsigned int uit;
typedef long long ll;
const int maxN = 1e5 + 7;
int N, a[maxN], pre[maxN][2][2] = {0};
int t[2][maxN<<2];
inline void pushup(int *tree, int rt) { tree[rt] = max(tree[lsn], tree[rsn]); }
void update(int *tree, int rt, int l, int r, int qx, int val)
{
    if(l == r) { tree[rt] = val; return; }
    int mid = HalF;
    if(qx <= mid) update(tree, Lson, qx, val);
    else update(tree, Rson, qx, val);
    pushup(tree, rt);
}
inline int query(int *tree, int rt, int l, int r, int ql, int qr)
{
    if(ql <= l && qr >= r) return tree[rt];
    int mid = HalF;
    if(qr <= mid) return query(tree, QL);
    else if(ql > mid) return query(tree, QR);
    else return max(query(tree, QL), query(tree, QR));
}
ll ans = 0;
inline void solve(int id)
{
    int l = 1, r = id - 1, mid = 0, ith = 0;
    while(l <= r)
    {
        mid = (l + r)>>1;
        if(query(t[0], 1, 1, N, mid, id - 1) > a[id])
        {
            l = mid + 1;
            ith = mid;
        }
        else r = mid - 1;
    }
    if(ith == 0) return;
    pre[id][0][0] = ith;
    l = 1; r = ith - 1; ith = 0;
    int nex_id = r;
    while(l <= r)
    {
        mid = (l + r)>>1;
        if(query(t[0], 1, 1, N, mid, nex_id) > a[id])
        {
            l = mid + 1;
            ith = mid;
        }
        else r = mid - 1;
    }
    if(ith == 0) return;
    pre[id][0][1] = ith;
}
inline void finsh(int id)
{
    int l = id + 1, r = N, mid = 0, ith = 0;
    while(l <= r)
    {
        mid = (l + r)>>1;
        if(query(t[1], 1, 1, N, id + 1, mid) > a[id])
        {
            r = mid - 1;
            ith = mid;
        }
        else l = mid + 1;
    }
    if(ith == 0) return;
    pre[id][1][0] = ith;
    l = ith + 1; r = N; ith = 0;
    int nex_id = l;
    while(l <= r)
    {
        mid = (l + r)>>1;
        if(query(t[1], 1, 1, N, nex_id, mid) > a[id])
        {
            r = mid - 1;
            ith = mid;
        }
        else l = mid + 1;
    }
    if(ith == 0) return;
    pre[id][1][1] = ith;
}
int main()
{
    scanf("%d", &N);
    for(int i=1; i<=N; i++) scanf("%d", &a[i]);
    for(int i=1; i<=N; i++) pre[i][1][1] = pre[i][1][0] = N + 1;
    update(t[0], 1, 1, N, 1, a[1]);
    for(int i=2; i<=N; i++)
    {
        solve(i);
        update(t[0], 1, 1, N, i, a[i]);
    }
    update(t[1], 1, 1, N, N, a[N]);
    for(int i=N-1; i>=1; i--)
    {
        finsh(i);
        update(t[1], 1, 1, N, i, a[i]);
    }
    for(int i=1; i<=N; i++)
    {
        ans += 1LL * (pre[i][0][0] - pre[i][0][1]) * (pre[i][1][0] - i) * a[i];
        ans += 1LL * (i - pre[i][0][0]) * (pre[i][1][1] - pre[i][1][0]) * a[i];
    }
    printf("%lld\n", ans);
    return 0;
}

 

你可能感兴趣的:(线段树,数据结构)