【bzoj】3745: [Coci2015]Norma

7 篇文章 0 订阅
4 篇文章 0 订阅

题意:

在这里插入图片描述
n <= 5e5

题解:

** 首先,想到确定最大值,最小值的区间一起统计。因为是统计所有区间,第一想法是枚举右端点,维护所有左端点的答案
用单调栈分别维护最小值,最大值,把贡献拆开讨论一下,需要用线段树维护mx(i) * mn(i), mx(i) * mn(i) * i,mx(i) * i,mn(i) * i,mx(i),mn(i) 6个和。因为更新的时候要么更新最大值,要么更新最小值。讨论一下该怎么修改。
**
**
正解是CDQ分治:
思路是一样的,但是CDQ分治写起来更加简便:
当前分治层只需要考虑的就是跨越当前中间节点的所有区间如何计算答案了。
从mid开始向左枚举左端点,考虑右端点的贡献。那么我们在右侧记录两个指针p,q,分别表示左侧的最大值和最小值第一次改变的位置。这两个指针会把整个序列分成三段。
第一段最大值和最小值都是左侧最大最小值,直接计算区间长度和就好了。
第二段是最大值和最小值中一个被改变了,分情况讨论一下,维护右侧的区间最大最小值就可以直接算了。第三部分是最大值和最小值都被改变了,那么把式子写出来,维护一个前缀就好了。
CDQ的思想也是固定mx和mn该怎么贡献,因为分治,所以可以方便的从mid把区间划分开
**

这题的细节一定要推清楚:区间是左闭右开的。推清楚细节后非常好写,然而我因为区间端点没有写对,还对拍了!

 
#include<bits/stdc++.h>
using namespace std;
 
#define rep(i,l,r) for(register int i = l ; i <= r ; i++)
#define repd(i,r,l) for(register int i = r ; i >= l ; i--)
#define rvc(i,S) for(register int i = 0 ; i < (int)S.size() ; i++)
#define rvcd(i,S) for(register int i = ((int)S.size()) - 1 ; i >= 0 ; i--)
#define fore(i,x)for (register int i = head[x] ; i ; i = e[i].next)
#define forup(i,l,r) for (register int i = l ; i <= r ; i += lowbit(i))
#define fordown(i,id) for (register int i = id ; i ; i -= lowbit(i))
#define pb push_back
#define prev prev_
#define stack stack_
#define mp make_pair
#define fi first
#define se second
#define lowbit(x) (x&(-x))
 
typedef long long ll;
typedef long double ld;
typedef unsigned long long ull;
typedef pair<int,int> pr;
 
const int inf = 2e8;
const int N = 3e6 + 10;
const int maxn = 500020;
const ll mod = 1e9;
 
ll sum1[maxn],sum2[maxn],sum3[maxn],sum4[maxn],sum5[maxn],sum6[maxn],ans;
int mx[maxn],mn[maxn],a[maxn],n;
 
inline void up(ll &x,ll y){ x = (x + y % mod + mod) % mod; }
inline ll sum(ll l,ll r){
    if ( l > r ) return 0;
    return ((l + r) * (r - l + 1) / 2) % mod;
}
void solve(int l,int r){
    if ( l == r ){
        up(ans,(ll)a[l] * a[r]);
        return;
    }
    int mid = (l + r) >> 1;
    solve(l,mid) , solve(mid + 1,r);
    mx[mid] = 0 , mn[mid] = inf;
    rep(i,mid,r + 1) sum1[i] = sum2[i] = sum3[i] = sum4[i] = sum5[i] = sum6[i] = 0;
    rep(i,mid + 1,r){
        mx[i] = max(mx[i - 1],a[i]);
        mn[i] = min(mn[i - 1],a[i]);    
        up(sum1[i],mx[i] + sum1[i - 1]);
        up(sum2[i],mn[i] + sum2[i - 1]);
        up(sum3[i],(ll)mx[i] * i + sum3[i - 1]);
        up(sum4[i],(ll)mn[i] * i + sum4[i - 1]);
        up(sum5[i],(ll)mx[i] * mn[i] + sum5[i - 1]);
        up(sum6[i],(ll)mx[i] * mn[i] % mod * i + sum6[i - 1]);
    }
    int p = mid + 1 , q = mid + 1;
    ll lmx = a[mid] , lmn = a[mid];
    repd(i,mid,l){
        lmx = max(lmx,(ll)a[i]) , lmn = min(lmn,(ll)a[i]);
        while ( mn[p] >= lmn && p <= r ) p++;
        while ( mx[q] <= lmx && q <= r ) q++;
        up(ans,sum(mid + 2 - i,min(p,q) - i) * lmn % mod * lmx);
        if ( p > q ) up(ans,lmn * (sum3[p - 1] - sum3[q - 1] - (i - 1) * (sum1[p - 1] - sum1[q - 1]) % mod));
        else{
        //      swap(p,q);
        //  这里不能swap,p,q指针分别是最小值和最大值,不能交换!想清楚!
            up(ans,lmx * (sum4[q - 1] - sum4[p - 1] - (i - 1) * (sum2[q - 1] - sum2[p - 1]) % mod));
        }
        if ( q <= r && p <= r ) up(ans,sum6[r] - sum6[max(p,q) - 1] - (i - 1) * (sum5[r] - sum5[max(q,p) - 1]));
        //注意如果最大值超过r,因为数组没有清空,会出错!
    }
}
int main(){
    scanf("%d",&n);
    rep(i,1,n) scanf("%d",&a[i]);
    solve(1,n);
    printf("%lld\n",ans);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值