牛客练习赛14B 区间的连续段
题目链接
题目大意
给定一个长度为 的序列 和一个常数
有 次询问
每次询问查询一个区间 内所有数最少分成多少个连续段
使得每段的和都 ,若无解则输出 ""
解题思路
简单回忆一下倍增求 思想:
- 表示以 为起点,往上跳 步后得到的祖先
- 因为往上跳 等价于先往上跳 步后再往上跳 步
- 所以可得:
回到这道题:
暴力的做法即遍历区间 ,贪心的让每段的长度尽可能大
考虑用倍增思想优化:
定义 表示:
以 为起点,分成 个连续段后,所能到达的最远位置的下一个位置(其中每个段的和都不超过 )
那么不难得到: ( 可通过二分前缀和得到
然后对于每个询问 :
先判断区间 是否存在 使得
这步我们维护一个权值数组的前缀和 判断
即当 时,
当 时,
当 则表示该区间存在 ,直接输出
若 则从高位往低位枚举 :
如果 则表示从 开始划分出 个连续段是 的
但是 连续段可能太多了(题目要求划分的连续段个数最少
所以就继续往下枚举如果 ,则表示从 开始划分出 个连续段是不够的
那就先划分出 个连续段,然后再从 的位置继续划分
即 ,
AC_Code
#include<bits/stdc++.h>
using namespace std;
const int N = 1e6 + 10;
int f[N][22];
int n , m , k , a[N] , sum[N];
long long pre[N];
signed main()
{
cin >> n >> m >> k;
for(int i = 1 ; i <= n ; i ++)
{
cin >> a[i] , pre[i] = pre[i - 1] + a[i];
sum[i] = sum[i - 1] + (a[i] > k);
}
for(int j = 0 ; j <= 21 ; j ++) f[n + 1][j] = n + 1;
for(int j = 0 ; j <= 21 ; j ++)
{
for(int i = 1 ; i <= n ; i ++)
{
f[i][0] = upper_bound(pre + i , pre + 1 + n , k - a[i] + pre[i]) - pre;
if(!j) continue ;
f[i][j] = f[f[i][j - 1]][j - 1];
}
}
while(m --)
{
int l , r , ans = 0;
cin >> l >> r;
if(sum[r] - sum[l - 1])
{
cout << "Chtholly\n";
continue ;
}
for(int j = 21 ; j >= 0 ; j --)
{
if(f[l][j] - 1 < r)
{
ans += 1 << j;
l = f[l][j];
}
}
cout << ans + 1 << '\n';
}
return 0;
}
凡所不能将我击倒的,都将使我更加强大
本文作者:GsjzTle
本文链接:https://www.cnblogs.com/GsjzTle/p/14702955.html
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
分类:
标签:
,
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】博客园携手 AI 驱动开发工具商 Chat2DB 推出联合终身会员
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· .NET Core 对象分配(Alloc)底层原理浅谈
· 聊一聊 C#异步 任务延续的三种底层玩法
· 敏捷开发:如何高效开每日站会
· 为什么 .NET8线程池 容易引发线程饥饿
· golang自带的死锁检测并非银弹
· 一个适用于 .NET 的开源整洁架构项目模板
· API 风格选对了,文档写好了,项目就成功了一半!
· 【开源】C#上位机必备高效数据转换助手
· .NET 9.0 使用 Vulkan API 编写跨平台图形应用
· MyBatis中的 10 个宝藏技巧!