HDU 6701 Make Rounddog Happy(ST表+启发式分治)

视觉狂
• 阅读 1097

启发式分治:给定$n$个数,求满足某种条件的点对数目或最大权值,而这个最大权值与点对$(a,b)$的区间$[a,b]$的区间最大/最小值有关。那么这时就可以考虑分治,对于区间$[L,R]$,找到最小/大值所在位置,然后处理横跨最小/大值所在位置的点对,然后递归处理子区间。

//#define LOCAL
#include <bits/stdc++.h>
using namespace std;
#define DBG printf("%d %s\n",__LINE__,__FUNCTION__)
#define ios ios::sync_with_stdio(false)
#define ctt cin.tie(0)
#define mem(t, v) memset ((t) , v, sizeof(t))
#define abs(x) ((x) >= 0 ? (x) : -(x))
#define LF putchar('\n')
#define SP putchar(' ')
#define eb emplace_back
#define mk make_pair
#define PI acos(-1.0)
#define INF 0x3f3f3f3f
#define eps 1e-6
#define ri register int
#define LL long long
#define ULL unsigned long long

template<typename T> 
void read(T &x) {x = 0;char ch = getchar();LL f = 1;while(!isdigit(ch)){if(ch == '-')f *= -1;ch = getchar();}while(isdigit(ch)){x = x * 10 + ch - 48; ch = getchar();}x *= f;}
template<typename T, typename... Args> 
void read(T &first, Args& ... args) {read(first);read(args...);}
template<typename T>
void write(T arg) {T x = arg;if(x < 0) {putchar('-'); x =- x;}if(x > 9) {write(x / 10);}putchar(x % 10 + '0');}
template<typename T, typename ... Ts>
void write(T arg, Ts ... args) {write(arg);if(sizeof...(args) != 0) {putchar(' ');write(args ...);}}

const LL MOD = 1e9 + 7;
const int MAXN = 3e5 + 50;

int T, n, k, arr[MAXN], dp[MAXN][25], pos[MAXN][25], pr[MAXN], pl[MAXN], buk[MAXN];
LL ans;

void getst() {  //ST表预处理出任意区间最大值的下标
    for(ri i = 1; i <= n; ++i)
        dp[i][0] = arr[i], pos[i][0] = i;
    for(ri j = 1; (1 << j) <= n; ++j)
        for(ri i = 1; i + (1 << j) - 1 <= n; ++i)
            if(dp[i][j-1] >= dp[i+(1<<(j-1))][j-1])
                dp[i][j] = dp[i][j-1], pos[i][j] = pos[i][j-1];
            else
                dp[i][j] = dp[i+(1<<(j-1))][j-1], pos[i][j] = pos[i+(1<<(j-1))][j-1];
}

int rmq(int l, int r) {
    int k = log2(r - l + 1);
    return dp[l][k] >= dp[r-(1<<k)+1][k] ? pos[l][k] : pos[r-(1<<k)+1][k];
}

void prework() {  //找到第i个元素左/右端第一个开始重复的元素
    mem(buk, 0);
    buk[arr[1]] = 1;
    int r = 2;
    for(ri i = 1; i <= n; ++i) {
        while(r <= n && !buk[arr[r]])
            buk[arr[r]] = 1, ++r;
        buk[arr[i]] = 0;
        pr[i] = r - 1;
    }
    buk[arr[n]] = 1;
    r = n - 1;
    for(ri i = n; i >= 1; --i) {
        while(r >= 1 && !buk[arr[r]])
            buk[arr[r]] = 1, --r;
        buk[arr[i]] = 0;
        pl[i] = r + 1;
    }
}

void solve(int l, int r) {  //分治
    if(l > r)
        return ;
    int mid = rmq(l, r);
    if(r - mid > mid - l) {
        for(ri i = l; i <= mid; ++i) {
            int d = max(i + arr[mid] - k - 1, mid);
            int cr = min(pr[i], r);
            if(cr >= d)
                ans += cr - d + 1;
        }
    } else {
        for(ri i = r; i >= mid; --i) {
            int d = min(mid, i - arr[mid] + k + 1);
            int cl = max(pl[i], l);
            if(d >= cl)
                ans += d - cl + 1;
        }
    }
    solve(l, mid - 1);//下标为mid的值左右都已经计算过,所以递归时不包括mid
    solve(mid + 1, r);
}

int main() {
#ifdef LOCAL
    freopen("data.in", "r", stdin);
    freopen("data.out", "w", stdout);
#endif
    read(T);
    while(T--) {
        read(n, k);
        for(ri i = 1; i <= n; ++i)
            read(arr[i]);
        getst();
        prework();
        ans = 0;
        solve(1, n);
        write(ans), LF;
    }
    return 0;
}
点赞
收藏
评论区
推荐文章
DaLongggggg DaLongggggg
4年前
python刷题-数列特征
问题描述给出n个数,找出这n个数的最大值,最小值,和。输入格式第一行为整数n,表示数的个数。第二行有n个数,为给定的n个数,每个数的绝对值都小于10000。输出格式输出三行,每行一个整数。第一行表示这些数中的最大值,第二行表示这些数中的最小值,第三行表示这些数的和。样例输入513245样例输出5211数据规模与约定1<n<10000。
桃浪十七丶 桃浪十七丶
4年前
C++写一个简单排序算法
分析算法步骤:1、暂定元素排列第0个为最小值,下标为min;2、然后从左往右依次扫描,与min的关键字比较,若比min的更小,则更新min下标为当前下标;3、并且把先前的最小值与当前找到目标的元素交换位置。cinclude<iostreamusingnamespacestd;voidSwap(int&a,int&b)inttem
Wesley13 Wesley13
4年前
mysql之数据分区
一:概述 通过把表分成多几区间,每个区间存储符合特定表达式的数据(即在我们创建分区表时指定每个分区存储的条件例如:PARTITIONp0VALUESLESSTHAN(100)即p0区间存储小于100的数据)。二:分区类型   即根据每个区间存储值的表达式不同,可分为如下几个类型,一般都是对数字类型或时间类型的数据进行分区。2.1 R
Stella981 Stella981
4年前
ElasticSearch
在Mysql中,我们可以获取一组数据的 最大值(Max)、最小值(Min)。同样我们能够对这组数据进行 分组(Group)。那么对于Elasticsearch中我们也可以实现同样的功能,聚合有关资料官方文档内容较多,这里大概分3篇或者4篇博客写这个有关Elasticsearch聚合。官方对聚合有四个关键字:
Stella981 Stella981
4年前
C语言 快速排序 Quick Sort
算法描述:快速排序一般是选择数组的第一个数据为对称轴参考值pivot。按照大小数组分割成左右两个区间。然后对左右两个区间再进行递归排序,知道结束为止。例子演示:数组:43251,长度:5,对称轴参考值选择第一个数据4。比它小的我们放到它的右边,比它大的我们放到左边。设置左右两个工作位置。指向开头和末尾。第一轮:4325
Stella981 Stella981
4年前
Gym102040 .Asia Dhaka Regional Contest(寒假自训第9场)
B.CountingInversion题意:给定L,R,求这个区间的逆序对数之和。(L,R<1e15)思路:一看这个范围就知道是数位DP。只是维护的东西稍微多一点,需要记录后面的各种数字的个数cnt,以及逆序对和sum,以及出现了多少种后缀num。那么枚举到当前位时,假设为i,那
Stella981 Stella981
4年前
CodeForces985F:Isomorphic Strings (字符串&hash)
题意:取出字符串Str里的两个串S,T,问对应位置的的字符在否有一一映射关系。hash:对于每个字符s‘a’‘z’,我们任意找一个i,满足Sis,(代码里用lower\_bound在区间找到最小的位置i)其对应的字符为Tit。然后我们判断这段区间里s的hash值是否等于t的hash值。不难证明26个字母都满足时对应hash相同时,才有
Wesley13 Wesley13
4年前
HDU 6345(子串查询 暴力)
题意是每组给定一个字符串,在有限查询次数内输出所要查询区间的字典序最小的子串个数。字典序最小的子串,就是所查询区间中字典序最小的单个字符,问题就转化成了求一段区间内字典序最小的字符个数。开始时盲目暴力,直接用桶排序的做法一段一段去求,果然t了(这种就不贴代码了)......然后想到先扫一遍,求出从字符串首位到第i位的最小字符数,再用一个数组存
深入理解线段树 | 京东物流技术团队
线段树(SegmentTree)是常用的维护区间信息的数据结构,它可以在O(logn)的时间复杂度下实现单点修改、区间修改、区间查询(区间求和、区间最大值或区间最小值)等操作,常用来解决RMQ问题。RMQ(RangeMinimum/MaximumQuery
小万哥 小万哥
1年前
C++ 数学函数、头文件及布尔类型详解
C数学C有许多函数可以让您在数字上执行数学任务。最大值和最小值max(x,y)函数可用于找到x和y的最大值:示例cppcout<<max(5,10);而min(x,y)函数可用于找到x和y的最小值:示例cppcout<<min(5,10);C头
深度学习 深度学习
8个月前
洛谷P2190题解:铁路售票系统车厢计算(差分数组+前缀和优化)
一、题目解读P2190题要求解决铁路售票系统中的车厢数量计算问题。题目给定n个车站和m条订票申请,每条申请包含区间实现区间修改的。通过差分记录每个站点的乘客变化(如进入/离开人数),再计算前缀和得到各站点的实时乘客数,从而找出最大值。特别处理环形区间(即
视觉狂
视觉狂
Lv1
林间新绿一重重,小蕾深藏数点红。
文章
4
粉丝
0
获赞
0