2019牛客暑期多校训练营(第七场)F-Energy stones(思维+树状数组)
题意:有n块能量石,每秒钟会增加Li的能量,但是一旦增长到了Ci它就不会增长了,它初始的能量为Ei。 现在有若干个时刻ti,会选择下标在[Si,Ti]的能量石吸取它们的能量,这些能量石的能量变为0,并依据上述规则继续增长。 问最后一共吸取了多少能量?
思路:本来我写的时候没考虑到时间复杂度,蜜汁自信觉得应该能跑出来,然并卵,我模拟了整个过程,毫无疑问的TLE了。这题的做法好像是被称作为线性期望性,大概可以理解为,比如有m次查询,我直接每次求多个数据的期望和,最后把这m次期望加起来就是答案,但是你发现每次求多个数据的期望的时候操作次数太多了,导致很难求或者时间复杂度过高。那么我们换个思路想一下,要是我们能分别求出每个数据在这m次查询中的期望,那么最后加起来不就是所有数据的期望了么。
这里我们考虑每块石头吸取了多少能量(即每个石头对答案的贡献)
假如我们能够维护出每块石头被吸取能量的若干个时间点,那么它们的增长能量的时间区间会是一条条线段(时间差)。
那么考虑这些线段长度大于等于[Ci/Li]的线段,它们的贡献是Ci,其他线段都没有长满,那么是ti*Li 。
那么考虑如何维护线段?用set + 树状数组
用一个set,考虑插入一个时间点会让一条原本的线段分裂成两段,删除一个时间点会让两个线段合并。(至多修改三个区间差)
那么对于一次吸取操作(ti,Si,Ti),相当于我们枚举到Si的时候插入ti这个时间点,枚举到Ti+1的时候删除ti这个时间点。
我自己在敲代码的时候就在想,你只在Si插入ti,那么之后的Si+1,Si+2. . . 那还有好多能量石怎么办,后来发现原来只是在没遇到Ti+1前一直保留这个时间点ti,这样就能保证在这个区间内每个点都能有这个时间点的信息
然后我们用树状数组维护这个线段
两个树状数组分别维护:
- 每个时间段一共有多少时间(可以求出<[Ci/Li]累计的时间)
- 每个时间段的个数(可以求出>=[Ci/Li]的时间段总数)
最后要注意Li等于0的情况,处理一下初始状态就可以了
关于计算初始状态,我自己开始看的时候也有些地方看不明白,这里解释一下:
对于计算每个石头的贡献,我们实际是由最开始一个时间点出发,然后不断到下一个时间点,直到最后一个时间点得出答案。如果合并成线段的话,就变成最开始的一个时间点加上相邻的点构成的线段也可以到达最后一个时间点。因此我们计算初始状态是用的第一个时间点,与我们树状数组存的时间差没有关系,不要搞混淆了。ans更新的时候s.size()-1减的就是第一个时间点。
感谢咖啡鸡 巨巨(大佬的意思~)的代码
Code
#include<bits/stdc++.h>
#define pb push_back
using namespace std;
typedef long long ll;
const int maxn=2e5+6; int t, n, m;
ll d, ans;
ll E[maxn], L[maxn], C[maxn], a[maxn], b[maxn];
vector<int> h[maxn];
set<int> s; //记录时间点
//求和
ll qry1(int x){
ll ret = 0;
while (x){
ret += a[x];
x -= x&(-x);
}
return ret;
}
ll qry2(int x){
ll ret = 0;
while (x){
ret += b[x];
x -= x&(-x);
}
return ret;
}
//更新树状数组
void Add(int x,int y){
while (x<maxn){
if (y>0) a[x]++; else a[x]--;
b[x]+=y;
x+=x&(-x);
}
}
//往set里加入时间点并对树状数组对应区间进行维护
void add(int x){
if (s.size()==0) {s.insert(x);return;}
auto p=s.lower_bound(x);
if (p==s.begin()){
int t=(*p)-x;
Add(t,t);
} else if (p==s.end()){
int t=x-(*(prev(p)));
Add(t,t);
} else {
int s=(*p)-x,t=x-(*(prev(p)));
Add(s,s);Add(t,t);
Add(s+t,-s-t);
}
s.insert(x);
}
//在set里删除时间点并对树状数组对应区间进行维护
void del(int x){
auto p=s.find(x);
if (s.size()==1) {s.erase(p);return;}
if (p==s.begin()){
int t=(*(next(p)))-x;
Add(t,-t);
} else if (p==prev(s.end())){
int t=x-(*(prev(p)));
Add(t,-t);
} else {
int s=(*(next(p)))-x;
int t=x-(*(prev(p)));
Add(t,-t);
Add(s,-s);
Add(t+s,t+s);
}
s.erase(p);
}
//初始化
void init()
{
memset(a,0,sizeof(a));
memset(b,0,sizeof(b));
for (int i=1;i<=n+1;i++) h[i].clear();
s.clear(); ans = 0;
}
int main()
{
int kase = 0;
scanf("%d", &t);
while (t--) {
scanf("%d",&n); init();
for (int i=1;i<=n;i++) scanf("%lld%lld%lld",&E[i],&L[i],&C[i]);
scanf("%d",&m);
while (m--){
int t,l,r;
scanf("%d%d%d",&t,&l,&r);
h[l].pb(t); h[r+1].pb(-t);
}
for (int i=1;i<=n+1;i++){
for (auto x:h[i]){
if (x>0) add(x); else del(-x);
}
if (!s.size()) continue;
ans+=min(C[i],E[i]+(*s.begin())*L[i]); //计算初始情况
if (L[i]==0) continue;
d=C[i]/L[i];
ans+=qry2(d)*L[i]+(s.size()-1-qry1(d))*C[i];
}
printf("Case #%d: %lld\n", ++kase, ans);
}
return 0;
}
附:迭代器的下一个或上一个分别用next()和prev()获取
这里用set是因为它插入删除效率比用其他序列容器高
2019牛客暑期多校训练营(第七场)F-Energy stones(思维+树状数组)的更多相关文章
- 2019牛客暑期多校训练营(第二场)J-Subarray(思维)
>传送门< 前言 这题我前前后后看了三遍,每次都是把网上相关的博客和通过代码认真看了再思考,然并卵,最后终于第三遍也就是现在终于看懂了,其实懂了之后发现其实没有那么难,但是的的确确需要思维 ...
- 2019牛客暑期多校训练营(第一场)I dp+线段树
题意 给出n个点,每个点有a,b两个属性,让你从左下角到右上角划一条线,线的左边每个点的贡献是\(a_i\),线的右边每个点的贡献是\(b_i\),使得两部分的总和最大. 分析 找一条折线将点分割开, ...
- 2019牛客暑期多校训练营(第三场)- F Planting Trees
题目链接:https://ac.nowcoder.com/acm/contest/883/F 题意:给定n×n的矩阵,求最大子矩阵使得子矩阵中最大值和最小值的差值<=M. 思路:先看数据大小,注 ...
- 2019牛客暑期多校训练营(第二场)E.MAZE(线段树+dp)
题意:给你一个n*m的矩阵 你只能向左向右相下走 有两种操作 q次询问 一种是把一个单位翻转(即可走变为不可走 不可走变为可走) 另一种是询问从(1,x) 走到 (n,y)有多少种方案 思路:题目n为 ...
- 2019牛客暑期多校训练营(第三场) F.Planting Trees(单调队列)
题意:给你一个n*n的高度矩阵 要你找到里面最大的矩阵且最大的高度差不能超过m 思路:我们首先枚举上下右边界,然后我们可以用单调队列维护一个最左的边界 然后计算最大值 时间复杂度为O(n*n*n) # ...
- 2019牛客暑期多校训练营(第九场)A:Power of Fibonacci(斐波拉契幂次和)
题意:求Σfi^m%p. zoj上p是1e9+7,牛客是1e9: 对于这两个,分别有不同的做法. 前者利用公式,公式里面有sqrt(5),我们只需要二次剩余求即可. 后者mod=1e9,5才 ...
- 2019牛客暑期多校训练营(第一场)A题【单调栈】(补题)
链接:https://ac.nowcoder.com/acm/contest/881/A来源:牛客网 题目描述 Two arrays u and v each with m distinct elem ...
- 2019牛客暑期多校训练营(第一场) B Integration (数学)
链接:https://ac.nowcoder.com/acm/contest/881/B 来源:牛客网 Integration 时间限制:C/C++ 2秒,其他语言4秒 空间限制:C/C++ 5242 ...
- 2019牛客暑期多校训练营(第一场) A Equivalent Prefixes ( st 表 + 二分+分治)
链接:https://ac.nowcoder.com/acm/contest/881/A 来源:牛客网 Equivalent Prefixes 时间限制:C/C++ 2秒,其他语言4秒 空间限制:C/ ...
- 2019牛客暑期多校训练营(第二场)F.Partition problem
链接:https://ac.nowcoder.com/acm/contest/882/F来源:牛客网 Given 2N people, you need to assign each of them ...
随机推荐
- 【C++】《C++ Primer 》第十六章
第十六章 模板与泛型编程 面向对象编程和泛型编程都能处理在编写程序时不知道类型的情况. OOP能处理类型在程序允许之前都未知的情况. 泛型编程在编译时就可以获知类型. 一.定义模板 模板:模板是泛型编 ...
- Jenkins上实现Python + Jenkins + Allure Report 接口自动化测试持续集成,最终测试报告用allure-report进行展示
项目介绍 接口功能测试应用:http://www.weather.com.cn/data/cityinfo/<city_code>.html 测试功能:获取对应城市的天气预报 源码:Pyt ...
- 为了加快速度,Redis都做了哪些“变态”设计
前言 列表对象是 Redis 中 5 种基础数据类型之一,在 Redis 3.2 版本之前,列表对象底层存储结构有两种:linkedlist(双端列表)和 ziplist(压缩列表),而在 Redis ...
- 针对Linux系统主机,进入修复模式,解决开机报错问题
1.让主机重启,进入开机时的内核选择界面,按e进入编辑界面 2.找到linux16那一行,将光标移动到最前面,按下End键,到这一行的末尾,然后空格 rd.break console=tty0 3.第 ...
- 全球城市ZoneId和UTC时间偏移量的最全对照表
前言 你好,我是A哥(YourBatman). 如你所知,现行的世界标准时间是UTC世界协调时,时区已不直接参与时间计算.但是呢,城市名称or时区是人们所能记忆和容易沟通的名词,因此我们迫切需要一个对 ...
- pytest学习笔记(pytest框架结构)
一.pytest框架中使用setup.teardown.更灵活按照用例级别可以分为以下几类: 1.模块级:(setup_module.teardown_module)在模块始末调用 2.函数级:(se ...
- HATEOAS的简单认识
HATEOAS: 超媒体作为应用程序状态引擎(HATEOAS)是REST应用程序体系结构的一个组件,它将其与其他网络应用程序体系结构区分开来. 使用HATEOAS,客户端与网络应用程序交互,其应用程序 ...
- pytest:conftest.py文件
一.fixture scope 为session 级别是可以跨 .py模块调用的,也就是当我们有多个 .py文件的用例时,如果多个用例只需调用一次fixture,可以将scope='session', ...
- (09)-Python3之--类的三大特性(封装、继承、多态)
1.封装 封装,就是只能在类的内部访问,外部访问属性或方法会报异常,python中的封装很简单,只要在属性前或者方法名前加上两个下划线就可以,如self.__name,def __eat(self)这 ...
- shell命令分隔符 二叉树结构的命令行树
shell命令分隔符 二叉树结构的命令行树 I ;&