一类分治问题

有一类关于区间最大值和最小值之类的问题,利用单调性,可以采用分治算法解决。

SPOJ22343 Norma

题意,给定一个数列,定义区间的代价为区间最大值、区间最小值、区间长度的成绩,求所有区间的代价和。

既然是分治,我们肯定要处理一个数列跨过中点的答案。

假设当前数列的中点为mid,我们从mid往前扫,扫到了i。

然后根据单调性,我们越往左扫,最大值单调不降,最小值单调不增。

那么我们可以在右边维护一个指针,表示满足最大值的区间的最靠右的端点。

假设有这么一种情况,那么我们可以把区间拆成mid~p1,p1~p2,p2~r,三段。

1、mid~p1,max和min都是已知的,max*min*(r-l+1)。

2、p1~p2,min是已知的,我们需要算一个sigma(max)再乘上区间长度搞个前缀和算一下。

3、maxmin都未知,我们还需要维护max*min*区间长度搞成前缀和。

Code

#include<iostream>
#include<cstdio>
#define N 500010
#define inf 1e18
using namespace std;
typedef long long ll;
const int mod=1e9;
ll ans,a[N],mn[N][2],mi[N][2],ji[N][2];
int n;
inline ll calc(ll l,ll r){
    return ((l+r)*(r-l+1)/2)%mod;
}
void solve(int l,int r){
    if(l==r){(ans+=a[l]*a[l]%mod)%=mod;return;}
    ll mid=(l+r)>>1;
    solve(l,mid);solve(mid+1,r);
    ll maxn=-inf,minn=inf;
    mn[mid][0]=mn[mid][1]=mi[mid][0]=mi[mid][1]=ji[mid][0]=ji[mid][1]=0;
    for(ll i=mid+1;i<=r;++i){
        maxn=max(maxn,a[i]);minn=min(minn,a[i]);
        mn[i][0]=(mn[i-1][0]+maxn)%mod;mn[i][1]=(mn[i-1][1]+maxn*(i-mid)%mod)%mod;
        mi[i][0]=(mi[i-1][0]+minn)%mod;mi[i][1]=(mi[i-1][1]+minn*(i-mid)%mod)%mod;
        ji[i][0]=(ji[i-1][0]+minn*maxn%mod)%mod;ji[i][1]=(ji[i-1][1]+minn*maxn%mod*(i-mid)%mod)%mod;
    }
    ll p=mid+1,q=mid+1;maxn=-inf;minn=inf;
    for(ll i=mid;i>=l;--i){
        minn=min(minn,a[i]);maxn=max(maxn,a[i]);
        while(p<=r&&a[p]>=minn)p++;
        while(q<=r&&a[q]<=maxn)q++;
        int ls=min(p,q),rs=max(p,q);
        (ans+=maxn*minn%mod*calc(mid+1-i+1,ls-i)%mod)%=mod;
        if(p<q)ans=(ans+((mi[rs-1][0]-mi[ls-1][0])*(mid-i+1)%mod+mi[rs-1][1]-mi[ls-1][1])%mod*maxn%mod+mod)%mod;
        if(p>q)ans=(ans+((mn[rs-1][0]-mn[ls-1][0])*(mid-i+1)%mod+mn[rs-1][1]-mn[ls-1][1])%mod*minn%mod+mod)%mod;
        ans=((ans+(ji[r][0]-ji[rs-1][0])*(mid-i+1)%mod+(ji[r][1]-ji[rs-1][1])%mod)%mod+mod)%mod;
    }
}
int main(){
    scanf("%d",&n);
    for(int i=1;i<=n;++i)scanf("%lld",&a[i]);
    solve(1,n);
    cout<<ans;
    return 0;
} 

CF526F

化简题意,给定一个排列,求有多少区间满足max-min=r-l;

和上题一样,考虑跨过中点的答案,因为我们要计算合法区间,所以这和max和min的分布情况有关。

最大值最小值都在一侧的情况,max,min,l都已知,r是可以O(1)求出的,直接判断就好了。

如果最大值最小值分布在两侧,我们可以在一边枚举最端点,在右边用双指针维护一个区间满足最大值在左边,最小值在右边。

然后max-min=r-l  ->  max+l=r+min用桶维护这个式子。

最大值最小值在右边,最小值在左边最大值在右边的情况同理。

Code

#include<iostream>
#include<cstdio>
#define N 300002
#define inf 0x3f3f3f3f
using namespace std;
int ma[N],mi[N],a[N],l,r,tong[N<<1],n;
long long ans;
void solve(int l,int r){
    if(l==r){ans++;return;}
    int mid=(l+r)>>1;
    solve(l,mid);solve(mid+1,r);
    ma[mid]=0;mi[mid]=inf;
    for(int i=mid+1;i<=r;++i){
        ma[i]=max(ma[i-1],a[i]);
        mi[i]=min(mi[i-1],a[i]);
    }
    ma[mid]=mi[mid]=a[mid];
    for(int i=mid-1;i>=l;--i){
        ma[i]=max(ma[i+1],a[i]);
        mi[i]=min(mi[i+1],a[i]);
    }
    int p=mid+1,q;
    for(int i=mid;i>=l;--i){
        p=ma[i]-mi[i]+i;
        if(p<=r&&p>=mid+1&&ma[p]<ma[i]&&mi[p]>mi[i])ans++;
    }
    p=mid;
    for(int i=mid+1;i<=r;++i){
        p=i-ma[i]+mi[i];
        if(p>=l&&p<=mid&&ma[p]<ma[i]&&mi[p]>mi[i])ans++;
    }
    p=q=mid;
    for(int i=mid+1;i<=r;++i){
        while(ma[p]<ma[i]&&p>=l)tong[mi[p]-p+n]++,p--;
        while(mi[q]>mi[i]&&q>p)tong[mi[q]-q+n]--,q--;
        ans+=tong[ma[i]-i+n];
    }
    while(q>p)tong[mi[q]-q+n]--,q--;
    p=q=mid+1;
    for(int i=mid;i>=l;--i){
        while(ma[p]<ma[i]&&p<=r)tong[mi[p]+p]++,p++;
        while(mi[q]>mi[i]&&q<p)tong[mi[q]+q]--,q++;
        ans+=tong[ma[i]+i];
    }
    while(q<p)tong[mi[q]+q]--,q++;
}
int main(){
    scanf("%d",&n);
    for(int i=1;i<=n;++i){
        scanf("%d%d",&l,&r);
        a[l]=r;
    }
    solve(1,n);
    cout<<ans;
    return 0;
}

原文地址:https://www.cnblogs.com/ZH-comld/p/9880982.html

时间: 2024-08-03 11:05:19

一类分治问题的相关文章

从《Cash》谈一类分治算法的应用

从<Cash>谈一类分治算法的应用 分治算法的基本思想是将一个规模为N的问题分解为K个规模较小的子问题,这些子问题相互独立且与原问题性质相同.求出子问题的解,就可得到原问题的解.分治算法非常基础,但是分治的思想却非常重要,本文将从今年NOI的一道动态规划问题Cash开始谈如何利用分治思想来解决一类与维护决策有关的问题: 例一.货币兑换(Cash) 问题描述 小Y最近在一家金券交易所工作.该金券交易所只发行交易两种金券:A纪念券(以下简称 A 券)和 B 纪念券(以下简称 B 券).每个持有金券

BZOJ1176---[Balkan2007]Mokia (CDQ分治 + 树状数组)

题目链接:http://www.lydsy.com/JudgeOnline/problem.php?id=1176 CDQ第一题,warush了好久.. CDQ分治推荐论文: 1 <从<Cash>谈一类分治算法的应用> 陈丹琦 2 <浅谈数据结构题的几个非经典解法>  许昊然 关于CDQ分治,两种要求:①操作不相互影响  ②可以离线处理 题目描述是有问题的,,初始时 全部为0,不是s 题意:二维平面内,两种操作,1 x y v ,位于(x,y)的值加上v...2 x1,

学习笔记: cdq分治

今年的课程有很大一部分内容是cdq分治及其扩展(也就是二进制分组),拜读后觉得还是蛮有用的,这里小小地总结一下.(话说自己草稿箱里还有好多学习笔记的半成品呢,真是弱爆了.顺便感谢下fy与wxl向我介绍了那么好的东西) 推荐论文: 1 <从<Cash>谈一类分治算法的应用> 陈丹琦 2 <浅谈数据结构题的几个非经典解法>  许昊然 Q: cdq分治和普通的分治有什么区别? A: 在我们平常使用的分治中,每一个子问题只解决它本身(可以说是封闭的).而在cdq分治中,对于划分

【BZOJ】1492: [NOI2007]货币兑换Cash(cdq分治)

http://www.lydsy.com/JudgeOnline/problem.php?id=1492 蒟蒻来学学cdq神算法啊.. 详见论文 陈丹琦<从<Cash>谈一类分治算法的应用> orz 此题表示被坑精度.....导致没1a...开小号交了几发....................坑. 蒟蒻就说说自己的理解吧.. 首先这题神dp...(表示完全看不出来) 首先我们要最大化钱,那么可以将问题转化为最大化A券!(或B券)!!!!这点太神了,一定要记住这些!! 设d[i]表

[BZOJ 1492][NOI2007]货币兑换Cash(CDQ分治+斜率优化Dp)

Description 小Y最近在一家金券交易所工作.该金券交易所只发行交易两种金券:A纪念券(以下简称A券)和 B纪念券(以下 简称B券).每个持有金券的顾客都有一个自己的帐户.金券的数目可以是一个实数.每天随着市场的起伏波动, 两种金券都有自己当时的价值,即每一单位金券当天可以兑换的人民币数目.我们记录第 K 天中 A券 和 B券 的 价值分别为 AK 和 BK(元/单位金券).为了方便顾客,金券交易所提供了一种非常方便的交易方式:比例交易法 .比例交易法分为两个方面:(a)卖出金券:顾客提

【模板】CDQ分治

其实我的CDQ分治写的和shi一样 参悟了好长时间才大概知道CDQ分治该怎么搞,按照网上的资料半抄半写弄了道BZOJ3262陌上花开,但是评测不了,只把样例给过了,所以仍然不知道这个板子是不是对的. 以下叙述都是博主从其他BLOG里东拼西凑的: CDQ分治用来解决一类可离线的问题,通常是有一堆奇奇怪怪的修改和询问,然后拿高级数据结构做来很恶心的题目. CDQ分治的基本套路: 1.把待处理区间[l,r]分为[l,mid]和[mid+1,r]两个区间,递归处理下去. 2.处理[l,mid]区间的修改

HDU 3842 Machine Works cdq分治 斜率优化

本题是利用cdq分治  实现斜率优化的一个题目 斜率优化之前做的几个题都是斜率单调,并且插入点时由于点在某一维单调,所以仅仅操作队首和队尾就能完成优化了 但是本题显然不是 主要参考了两个东西 从<Cash>谈一类分治算法的应用 (Day1)cdq分治相关 这两个直接在百度上搜 ,第一个出来的就是 本题的题意是 一个公司获得了一个厂房n(10^5)天的使用权 和一笔启动资金C(10^9),准备在n天里租借机器生产来获得收益 可以租借的机器有M(10^5)个,每个机器有四个值,D,P,R,G (D

CDQ分治题目小结

CDQ分治属于比较特殊的一类分治,许多问题转化为这类分治的时候,时空方面都会有很大节省,而且写起来没有这么麻烦. 这类分治的特殊性在于分治的左右两部分的合并,作用两部分在合并的时候作用是不同的,比如,通过左半部分的影响来更新右半部分,所以分治开始前都要按照某一个关键字排序,然后利用这个顺序,考虑一个区间[l, r]的两部分间的影响.感觉说的太多,还是不如具体题目分析,而且题目也不尽相同,记住几句话是没什么用的. 练习地址: http://vjudge.net/contest/view.actio

线段树分治

2014徐寅展论文<线段树在一类分治问题上的应用>读后感. 线段树分治 线段树分治其实就是有撤销操作的时间分治. 题目让你维护一些信息,每次可以询问,可以执行一种操作,也可以将之前的某个这种操作撤回. 操作容易维护,但撤回操作不容易维护. 需要将操作,询问都离线下来.将时间轴画出来,那么每个操作只在时间轴上的一个区间内生效. 用线段树给这个区间打上这个操作的标记,维护信息. TJOI2018 数学计算 小豆现在有一个数x,初始值为1. 小豆有Q次操作,操作有两种类型: m: x = x * m