美味佳肴【可持久化线段树】【“纽劢科技杯”第十六届同济大学程序设计竞赛暨上海邀请赛.K题】

题目链接


题目描述

    众所周知,天才程序员菜哭武是一个伟大的厨师。这天,张老师和石头来到菜哭武家做客,想尝一尝菜哭武的手艺。
菜哭武手上有n种食材,每种食材个数无限多,编号为i的食材有一个美味度ai。一道菜中,每种编号的食材至多有一个,
而这道菜的美味度是这道菜包含的食材的美味度之和。
    每次张老师会指定一个编号l, 石头会指定一个编号r(l <= r),然后菜哭武会在编号在[l, r]中的食材中选若干
种食材做菜。张老师和石头都是美食家,因此他们要求菜哭武依次做出美味度以1,2,3,4...这样依次加1递增的菜,
直到菜哭武用当前的食材无法做出美味度为x的菜。比如,某一个[l, r]中的食材的美味度依次为2, 1, 1, 1,7,那么
美味度为1, 2, 3, 4, 5的菜都是可以做出来的,而美味度为6的菜是无法做出来的,所以对于这对[l, r],x = 6。
    张老师和石头一共会提出m个这样的[l, r]区间,对于每个区间,菜哭武想知道这个x是多少。

输入描述:

    第一行一个整数n(1 <= n <= 105), 代表食材的个数。
    第二行n个整数,用空格隔开,第i个整数ai代表编号为i的食材的美味度。(Σai <= 109)
    第三行一个整数m(1 <= m <= 105),代表区间的个数。
    接下来m行,每行一对整数l, r,代表一个区间。(1 <= l <= r <= n)

输出描述:

对于每一个区间,输出一行对应的答案。

  很不错的一道题,一个问区间的连续上升的序列可以从1开始上升到哪一个值刚好就没有这个值。

我们可以随便列举一串数,可以发现会有这样的规律:

例一:1,1,4。我们发现,我们可以先找到1,然后再往上就是可以(1+1)得到2,但是我们可以发现构不成3,岂不是尴尬。所以是3.

例二:1,1,1,4。我们发现,这次我们就可以找到3了,(1+1+1)就可以直接得到3了,然后我们再去找4,发现可以找到4,然后向后推到的最大的就是(7),所以,我们找不到8,输出8。

例三:1,1,3,4。这次,我们通过(1+1)可以先找到2,然后再去找3,发现我们可以找到3,并且由3和比3小的数,我们组成的最大的数就是(5),然后既然能找到5,说明我们接下去只需要再找到6就可以继续往下推了,我们会不会找到6呢?(1+1+4)也就是6了,但是我们其实可以组成到(1+1+3+4 = 9),再往后,我们发现就组不成其他的东西了,所以答案就是10。

例四:2,3,5,7。我们发现这时候最好从0开始找,那么我们就是要先去找1,看看1存不存在,然而,我们发现1并不存在,那么就是意味着,输出为1。

……

从中我们就可以发现一些些的规律了,这个也正是我所需要的。

  就像找零一样(这可不一定是个好比喻),我们总是要从最小的开始,然后向上叠加,那么我们就是要知道最小的数的和,然后我们才能再向上叠加,也就是我们现在在(1~X)是充满的话,我们现在要去找(X+1),我们可能会发现多个(X+1),那么便都加进来,我们假设这样的正整数N,我们如果找到有这样的(X+1),那么也就是意味着(1~X+1)是充满的,如果还有多个(X+1),那么我们不如可以看作是(X+1~2*(X+1)-1),因为我们本来就是有(X)的,我们既然能填充X,也就能填充这些,以此不断的类推即可。

 那么就是不断的维护前缀和了,这才是最棘手的事情,如何去不断的维护前缀和呢?时间戳来维护差值吧,然后每个时间戳下面记录的对应的位置,对应的位置上的对应的数的权值累加,譬如说这里有个7,那么当我们再添加一个7的时候,就是在7这号位置上“+7”,变成了14。就是这样的一个思维,我们可以不断的去寻找最大上线的填充了。接下去所要做的就是一个维护一颗可持久化线段树,不同的时间戳的差值,也就是我们最后所要寻找的答案了。


#include <iostream>
#include <cstdio>
#include <cmath>
#include <string>
#include <cstring>
#include <algorithm>
#include <limits>
#include <vector>
#include <stack>
#include <queue>
#include <set>
#include <map>
#define lowbit(x) ( x&(-x) )
#define pi 3.141592653589793
#define e 2.718281828459045
#define INF 0x3f3f3f3f
#define HalF (l + r)>>1
#define lsn rt<<1
#define rsn rt<<1|1
#define Lson lsn, l, mid
#define Rson rsn, mid+1, r
#define QL Lson, ql, qr
#define QR Rson, ql, qr
#define myself rt, l, r
using namespace std;
typedef unsigned long long ull;
typedef long long ll;
const int maxN = 1e5 + 7, _UP = 1e9;
int N, M, root[maxN], tot, lc[40*maxN], rc[40*maxN], tree[40*maxN];
inline void insert(int &rt, int old, int l, int r, int pos)
{
    rt = ++tot;
    tree[rt] = tree[old] + pos;
    lc[rt] = lc[old];   rc[rt] = rc[old];
    if(l == r) return;
    int mid = HalF;
    if(pos <= mid) insert(lc[rt], lc[old], l, mid, pos);
    else insert(rc[rt], rc[old], mid + 1, r, pos);
}
inline int Query(int x, int y, int l, int r, int pos)
{
    if(l == r) return tree[x] - tree[y];
    int mid = HalF;
    if(pos <= mid) return Query(lc[x], lc[y], l, mid, pos);
    else return tree[lc[x]] - tree[lc[y]] + Query(rc[x], rc[y], mid + 1, r, pos);
}
inline void init()
{
    tot = 0;
    memset(lc, 0, sizeof(lc));
    memset(rc, 0, sizeof(rc));
    memset(tree, 0, sizeof(tree));
}
int main()
{
    scanf("%d", &N);
    init();
    for(int i=1, u; i<=N; i++)
    {
        scanf("%d", &u);
        insert(root[i], root[i-1], 1, _UP, u);
    }
    scanf("%d", &M);
    int sum = 0, l, r, tmp;
    while(M--)
    {
        scanf("%d%d", &l, &r);
        sum = 0;
        while(sum < _UP)
        {
            tmp = Query(root[r], root[l-1], 1, _UP, sum + 1);
            if(sum == tmp) break;
            sum = tmp;
        }
        printf("%d\n", sum + 1);
    }
    return 0;
}

 

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
完整版:https://download.csdn.net/download/qq_27595745/89522468 【课程大纲】 1-1 什么是java 1-2 认识java语言 1-3 java平台的体系结构 1-4 java SE环境安装和配置 2-1 java程序简介 2-2 计算机中的程序 2-3 java程序 2-4 java类库组织结构和文档 2-5 java虚拟机简介 2-6 java的垃圾回收器 2-7 java上机练习 3-1 java语言基础入门 3-2 数据的分类 3-3 标识符、关键字和常量 3-4 运算符 3-5 表达式 3-6 顺序结构和选择结构 3-7 循环语句 3-8 跳转语句 3-9 MyEclipse工具介绍 3-10 java基础知识章节练习 4-1 一维数组 4-2 数组应用 4-3 多维数组 4-4 排序算法 4-5 增强for循环 4-6 数组和排序算法章节练习 5-0 抽象和封装 5-1 面向过程的设计思想 5-2 面向对象的设计思想 5-3 抽象 5-4 封装 5-5 属性 5-6 方法的定义 5-7 this关键字 5-8 javaBean 5-9 包 package 5-10 抽象和封装章节练习 6-0 继承和多态 6-1 继承 6-2 object类 6-3 多态 6-4 访问修饰符 6-5 static修饰符 6-6 final修饰符 6-7 abstract修饰符 6-8 接口 6-9 继承和多态 章节练习 7-1 面向对象的分析与设计简介 7-2 对象模型建立 7-3 类之间的关系 7-4 软件的可维护与复用设计原则 7-5 面向对象的设计与分析 章节练习 8-1 内部类与包装器 8-2 对象包装器 8-3 装箱和拆箱 8-4 练习 9-1 常用类介绍 9-2 StringBuffer和String Builder类 9-3 Rintime类的使用 9-4 日期类简介 9-5 java程序国际的实现 9-6 Random类和Math类 9-7 枚举 9-8 练习 10-1 java异常处理 10-2 认识异常 10-3 使用try和catch捕获异常 10-4 使用throw和throws引发异常 10-5 finally关键字 10-6 getMessage和printStackTrace方法 10-7 异常分类 10-8 自定义异常类 10-9 练习 11-1 Java集合框架和泛型机制 11-2 Collection接口 11-3 Set接口实现类 11-4 List接口实现类 11-5 Map接口 11-6 Collections类 11-7 泛型概述 11-8 练习 12-1 多线程 12-2 线程的生命周期 12-3 线程的调度和优先级 12-4 线程的同步 12-5 集合类的同步问 12-6 用Timer类调度任务 12-7 练习 13-1 Java IO 13-2 Java IO原理 13-3 流类的结构 13-4 文件流 13-5 缓冲流 13-6 转换流 13-7 数据流 13-8 打印流 13-9 对象流 13-10 随机存取文件流 13-11 zip文件流 13-12 练习 14-1 图形用户界面设计 14-2 事件处理机制 14-3 AWT常用组件 14-4 swing简介 14-5 可视开发swing组件 14-6 声音的播放和处理 14-7 2D图形的绘制 14-8 练习 15-1 反射 15-2 使用Java反射机制 15-3 反射与动态代理 15-4 练习 16-1 Java标注 16-2 JDK内置的基本标注类型 16-3 自定义标注类型 16-4 对标注进行标注 16-5 利用反射获取标注信息 16-6 练习 17-1 顶目实战1-单机版五子棋游戏 17-2 总体设计 17-3 代码实现 17-4 程序的运行与发布 17-5 手动生成可执行JAR文件 17-6 练习 18-1 Java数据库编程 18-2 JDBC类和接口 18-3 JDBC操作SQL 18-4 JDBC基本示例 18-5 JDBC应用示例 18-6 练习 19-1 。。。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Wuliwuliii

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值