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 ...
随机推荐
- 容器编排系统K8s之crd资源
前文我们了解了k8s节点污点和pod的对节点污点容忍度相关话题,回顾请参考:https://www.cnblogs.com/qiuhom-1874/p/14255486.html:今天我们来聊一下扩展 ...
- 【Java基础】Eclipse 和数组
Eclipse 和数组 Eclipse 安装和使用 ... 数组的概述 数组(Array):是多个相同类型数据按一定顺序排列的集合,并使用一个名字命名,并通过编号的方式对这些数据进行统一管理. 数组相 ...
- 【JDBC核心】实现 CRUD 操作
实现 CRUD 操作 操作和访问数据库 数据库连接被用于向数据库服务器发送命令和 SQL 语句,并接受数据库服务器返回的结果.其实一个数据库连接就是一个 Socket 连接. java.sql 包中有 ...
- LeetCode662 二叉树最大宽度
给定一个二叉树,编写一个函数来获取这个树的最大宽度.树的宽度是所有层中的最大宽度.这个二叉树与满二叉树(full binary tree)结构相同,但一些节点为空. 每一层的宽度被定义为两个端点(该层 ...
- Linux学习笔记 | 常见错误之账户密码正确但是登录不进去系统
前言: 笔者今日由于Linux版本的原因,需要Linux内核版本不能太高的系统,而日常使用的ubuntu系统不能满足需求,于是新建了一个虚拟机,选用的系统是Ubuntu16的,配置了一下午的各种依赖环 ...
- 【设计模式】Java设计模式精讲之原型模式
简单记录 - 慕课网 Java设计模式精讲 Debug方式+内存分析 & 设计模式之禅-秦小波 文章目录 1.原型模式的定义 原型-定义 原型-类型 2.原型模式的实现 原型模式的通用类图 原 ...
- 入门OJ:郭嘉的消息传递
题目描述 我们的郭嘉大大在曹操这过得逍遥自在,但是有一天曹操给了他一个任务,在建邺城内有N(<=1000)个袁绍的奸细 将他们从1到N进行编号,同时他们之间存在一种传递关系,即若C[i,j]=1 ...
- Zju1100 Mondriaan
题目描述 有一个m行n列的矩阵,用1*2的骨牌(可横放或竖放)完全覆盖,骨牌不能重叠,有多少种不同的覆盖的方法? 你只需要求出覆盖方法总数mod p的值即可. 输入格式 三个整数数n,m,p,m< ...
- secrets 管理工具 Vault 的介绍、安装及使用
原文:https://ryan4yin.space/posts/expirence-of-vault/ Vault 是 hashicorp 推出的 secrets 管理.加密即服务与权限管理工具.它的 ...
- HTML5与CSS3知识点总结
好好学习,天天向上 本文已收录至我的Github仓库DayDayUP:github.com/RobodLee/DayDayUP,欢迎Star 原文链接:https://blog.csdn.net/we ...