【BZOJ3745】【COCI2015】Norma(分治)

1 篇文章 0 订阅
1 篇文章 0 订阅

Description


Solution

考虑分治,假设我们现在分治到区间 [l,r] [ l , r ] ,区间中点为 mid m i d ,那么只要计算跨过中间的区间的贡献即可。

我们用一个 x x mid l l 枚举,设mini=xmidai=Amaxi=xmidai=B
我们维护两个指针 p,q p , q mid m i d 向右移动, p p 维护最右到哪个位置满足mini=mid+1paiA;同理, q q 维护最右到哪个位置满足maxi=mid+1qaiB
对于当前的左端点 x x ,我们将右区间分为3段(这里假设 pq p ≤ q q<p q < p 同 理 ):
1. 对于 yp y ≤ p ,最小值为 A A ,最大值为B,可直接求和。
2. 对于 p<yq p < y ≤ q ,最大值为 B B ,最小值未知,我们要算的贡献是:y=p+1qB(yx+1)maxi=mid+1yai。我们预处理出 maxi=mid+1yai,maxi=mid+1yi×ai max i = m i d + 1 y a i , max i = m i d + 1 y i × a i 的前缀和即可。
(同理要预处理 mini=mid+1yai,mini=mid+1yi×ai min i = m i d + 1 y a i , min i = m i d + 1 y i × a i 的前缀和)
3. q<y q < y 时,贡献为 y=q+1r(yx+1))maxi=mid+1yaimini=mid+1yai ∑ y = q + 1 r ( y − x + 1 ) ) max i = m i d + 1 y a i min i = m i d + 1 y a i ,我们预处理出 maxi=mid+1yaimini=mid+1yai max i = m i d + 1 y a i min i = m i d + 1 y a i maxi=mid+1yaimini=mid+1yai×i max i = m i d + 1 y a i min i = m i d + 1 y a i × i 的前缀和即可。


Solution

/************************************************
 * Au: Hany01
 * Date: Aug 17th, 2018
 * Prob: BZOJ3745 COCI2015 Norma
 * Email: hany01@foxmail.com
 * Inst: Yali High School
************************************************/

#include<bits/stdc++.h>

using namespace std;

typedef long long LL;
typedef long double LD;
typedef pair<int, int> PII;
#define rep(i, j) for (register int i = 0, i##_end_ = (j); i < i##_end_; ++ i)
#define For(i, j, k) for (register int i = (j), i##_end_ = (k); i <= i##_end_; ++ i)
#define Fordown(i, j, k) for (register int i = (j), i##_end_ = (k); i >= i##_end_; -- i)
#define Set(a, b) memset(a, b, sizeof(a))
#define Cpy(a, b) memcpy(a, b, sizeof(a))
#define x first
#define y second
#define pb(a) push_back(a)
#define mp(a, b) make_pair(a, b)
#define SZ(a) ((int)(a).size())
#define INF (0x3f3f3f3f)
#define INF1 (2139062143)
#define debug(...) fprintf(stderr, __VA_ARGS__)
#define y1 wozenmezhemecaia

template <typename T> inline bool chkmax(T &a, T b) { return a < b ? a = b, 1 : 0; }
template <typename T> inline bool chkmin(T &a, T b) { return b < a ? a = b, 1 : 0; }

inline int read() {
    static int _, __; static char c_;
    for (_ = 0, __ = 1, c_ = getchar(); c_ < '0' || c_ > '9'; c_ = getchar()) if (c_ == '-') __ = -1;
    for ( ; c_ >= '0' && c_ <= '9'; c_ = getchar()) _ = (_ << 1) + (_ << 3) + (c_ ^ 48);
    return _ * __;
}

const int maxn = 5e5 + 3, MOD = 1e9;

int n, a[maxn], mn[maxn], mx[maxn], mnid[maxn], mxid[maxn], mnmx[maxn], mnmxid[maxn], Ans;

inline int ad(int x, int y) { if ((x += y) >= MOD) return x - MOD; return x; }

void DivideAndConquer(int l, int r)
{
    if (l == r) { Ans = ad(Ans, (LL)a[l] * a[l] % MOD); return; }

    int mid = (l + r) >> 1;
    DivideAndConquer(l, mid), DivideAndConquer(mid + 1, r);

    register int p = mid, q = mid, A = INF, B = -INF, minpq, maxpq, Min = INF, Max = -INF;

    mn[mid] = mnid[mid] = mx[mid] = mxid[mid] = mnmx[mid] = mnmxid[mid] = 0;
    For(i, mid + 1, r) {
        chkmin(Min, a[i]), chkmax(Max, a[i]);
        mn[i] = ad(mn[i - 1], Min), mnid[i] = ad(mnid[i - 1], (LL)Min * i % MOD);
        mx[i] = ad(mx[i - 1], Max), mxid[i] = ad(mxid[i - 1], (LL)Max * i % MOD);
        mnmx[i] = ad(mnmx[i - 1], (LL)Min * Max % MOD), mnmxid[i] = ad(mnmxid[i - 1], (LL)Min * Max % MOD * i % MOD);
    }

    Fordown(x, mid, l) {
        chkmax(B, a[x]), chkmin(A, a[x]);
        while (p < r && a[p + 1] >= A) ++ p;
        while (q < r && a[q + 1] <= B) ++ q;
        minpq = min(p, q), maxpq = max(p, q);
        if (mid < minpq) Ans = ad(Ans, (LL)(mid + 1 - x + 1 + minpq - x + 1) * (minpq - mid) / 2 % MOD * A % MOD * B % MOD);
        if (minpq < maxpq) {
            if (p < q) Ans = ad(Ans, (LL)B * ad(ad(mnid[maxpq], MOD - mnid[minpq]), MOD - (LL)ad(mn[maxpq], MOD - mn[minpq]) * (x - 1) % MOD) % MOD);
            else       Ans = ad(Ans, (LL)A * ad(ad(mxid[maxpq], MOD - mxid[minpq]), MOD - (LL)ad(mx[maxpq], MOD - mx[minpq]) * (x - 1) % MOD) % MOD);
        }
        Ans = ad(Ans, ad(ad(mnmxid[r], MOD - mnmxid[maxpq]), MOD - (LL)(x - 1) * ad(mnmx[r], MOD - mnmx[maxpq]) % MOD));
    }
}

int main()
{
#ifdef hany01
    freopen("bzoj3745.in", "r", stdin);
    freopen("bzoj3745.out", "w", stdout);
#endif

    For(i, 1, n = read()) a[i] = read();
    DivideAndConquer(1, n), printf("%d\n", Ans);

    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值