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 ...
随机推荐
- Soat控制HAProxy 动态增减服务器
Soat控制HaProxy 动态增减服务器 安装HaProxy-1.5.18: yum install haproxy -y yum install socat -y HaProxy-1.5.18 配 ...
- Linux学习笔记 | 配置Samba
Samba是在Linux和UNIX系统上实现SMB协议的一个免费软件,由服务器及客户端程序构成.SMB(Server Messages Block,信息服务块)是一种在局域网上共享文件和打印机的一种通 ...
- (十)Python装饰器
装饰器:本质就是函数,功能是为其他函数添加附加功能. 两个原则: 1.不修改被修饰函数的源代码 2.不修改被修饰函数的调用方式 一个栗子 def test(): res = 0 for i in ra ...
- MAVEN编译NIFI源码
场景: 由于项目需求,需要借用NIFI进行二次开发,因此需要将NIFI源码进行修改,然后编译,办公环境无外网. 步骤: (1) 找一台可以上网(外网)的机器,安装java环境和maven环境,安装 ...
- Android之旅2
一.动静态调试四大组件 (一).activity 一个又一个的界面,需要在manifest里面注册 (二). (三).service (四).broadcast receiver 二.开始分析 1.先 ...
- 如何使用github搜索需要的开源项目
按照项目名/仓库名搜索(大小写不敏感)in:name xxx按照README搜索(大小写不敏感)in:readme xxx按照description搜索(大小写不敏感)in:description x ...
- 集成多种协议、用于 USB-A 和 TYPE-C 双端口输出的快充协议芯片IP2726
1. 特性 支持 1A1C 支持 USB-A 和 TYPE-C 双端口输出 单口输出支持全部快充协议 双口同时插入时降压到 5V 快充规格 集成 QC2.0/QC3.0/QC4/QC4+输 ...
- 学习Java第三天
方法重载:同一个类,方法名相同,参数不同(个数不同,类型不同,顺序不同),判断是否重载,只看方法名和参数,跟返回值无关. IDEA查看方法源代码:Crtl + 鼠标左键 进制表示 Java数值默认为十 ...
- nfs samba文件共享服务
(注意:实验之前强关闭selinux和防火墙) 一丶nfs ① 1.服务端 启动服务 systemctl start nfs.service 配置文件 vim /etc/exports share ...
- Linux系统设置 SSH 通过密钥登录
我们一般使用 PuTTY 等 SSH 客户端来远程管理 Linux 服务器.但是,一般的密码方式登录,容易有密码被暴力破解的问题.所以,一般我们会将 SSH 的端口设置为默认的 22 以外的端口,或者 ...