>传送门<

题意: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+1Si+2. . . 那还有好多能量石怎么办,后来发现原来只是在没遇到Ti+1前一直保留这个时间点ti,这样就能保证在这个区间内每个点都能有这个时间点的信息

然后我们用树状数组维护这个线段

两个树状数组分别维护:

  1. 每个时间段一共有多少时间(可以求出<[Ci/Li]累计的时间)
  2. 每个时间段的个数(可以求出>=[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(思维+树状数组)的更多相关文章

  1. 2019牛客暑期多校训练营(第二场)J-Subarray(思维)

    >传送门< 前言 这题我前前后后看了三遍,每次都是把网上相关的博客和通过代码认真看了再思考,然并卵,最后终于第三遍也就是现在终于看懂了,其实懂了之后发现其实没有那么难,但是的的确确需要思维 ...

  2. 2019牛客暑期多校训练营(第一场)I dp+线段树

    题意 给出n个点,每个点有a,b两个属性,让你从左下角到右上角划一条线,线的左边每个点的贡献是\(a_i\),线的右边每个点的贡献是\(b_i\),使得两部分的总和最大. 分析 找一条折线将点分割开, ...

  3. 2019牛客暑期多校训练营(第三场)- F Planting Trees

    题目链接:https://ac.nowcoder.com/acm/contest/883/F 题意:给定n×n的矩阵,求最大子矩阵使得子矩阵中最大值和最小值的差值<=M. 思路:先看数据大小,注 ...

  4. 2019牛客暑期多校训练营(第二场)E.MAZE(线段树+dp)

    题意:给你一个n*m的矩阵 你只能向左向右相下走 有两种操作 q次询问 一种是把一个单位翻转(即可走变为不可走 不可走变为可走) 另一种是询问从(1,x) 走到 (n,y)有多少种方案 思路:题目n为 ...

  5. 2019牛客暑期多校训练营(第三场) F.Planting Trees(单调队列)

    题意:给你一个n*n的高度矩阵 要你找到里面最大的矩阵且最大的高度差不能超过m 思路:我们首先枚举上下右边界,然后我们可以用单调队列维护一个最左的边界 然后计算最大值 时间复杂度为O(n*n*n) # ...

  6. 2019牛客暑期多校训练营(第九场)A:Power of Fibonacci(斐波拉契幂次和)

    题意:求Σfi^m%p. zoj上p是1e9+7,牛客是1e9:  对于这两个,分别有不同的做法. 前者利用公式,公式里面有sqrt(5),我们只需要二次剩余求即可.     后者mod=1e9,5才 ...

  7. 2019牛客暑期多校训练营(第一场)A题【单调栈】(补题)

    链接:https://ac.nowcoder.com/acm/contest/881/A来源:牛客网 题目描述 Two arrays u and v each with m distinct elem ...

  8. 2019牛客暑期多校训练营(第一场) B Integration (数学)

    链接:https://ac.nowcoder.com/acm/contest/881/B 来源:牛客网 Integration 时间限制:C/C++ 2秒,其他语言4秒 空间限制:C/C++ 5242 ...

  9. 2019牛客暑期多校训练营(第一场) A Equivalent Prefixes ( st 表 + 二分+分治)

    链接:https://ac.nowcoder.com/acm/contest/881/A 来源:牛客网 Equivalent Prefixes 时间限制:C/C++ 2秒,其他语言4秒 空间限制:C/ ...

  10. 2019牛客暑期多校训练营(第二场)F.Partition problem

    链接:https://ac.nowcoder.com/acm/contest/882/F来源:牛客网 Given 2N people, you need to assign each of them ...

随机推荐

  1. 第9章 集合处理(数组、Map、Set)

    目录 1. 数组 1.1 创建数组 1.2 在数组两端添加删除元素 1.3 在数组任意位置添加.删除元素 delete删除数组元素无效 使用splice方法增.删.改元素 1.4 数组的常用操作 数组 ...

  2. 《犬夜叉2021》我想通过Binder找到你

    前言 本故事纯属虚构,如有不通顺的逻辑请轻喷.️ <犬夜叉2021> 第一章:我还能找到你吗,阿篱 犬夜叉和奈落大决战之后,四魂之玉.食骨之井消失,谁也不知道去了哪,而犬夜叉和阿篱再次被分 ...

  3. TCP/IP五层模型-传输层-TCP协议

    ​1.定义:TCP是一种面向连接.可靠的.基于字节流的传输控制协议. 2.应用场景:TCP为可靠传输,适合对数据完整性要求高,对延时不敏感的场景,比如邮件. 3.TCP报文:①TCP报文格式: ②TC ...

  4. 有了链路日志增强,排查Bug小意思啦!

    在工作中,相信大家最怕的一件事就是听到有人在工作群艾特你:某某功能报错啦... 然后你就得屁颠屁颠的去服务器看日志,日志量少还好点,多的话找起来太麻烦了.不太容易直接定位到关键地方. 东找找西找找,好 ...

  5. 诸葛 VS 庞统,拿下 Paxos 共识算法

    前言 分布式确实是一个有趣的话题,只要你留心观察,分布式在生活中无处不在. 悟空哥最开始学习分布式是从一篇非常用心写的技术征文开始的,而且这篇文章获得了征文第一名,在此感谢掘金社区提供的平台.想学习的 ...

  6. Hbase snapshot数据迁移

    # 在源集群中创建快照(linux shell) hbase snapshot -t <table_name> -n <snapshot_name> 或(hbase shell ...

  7. ps ww

    [root@ma ~]# ps ww -p 1 PID TTY STAT TIME COMMAND 1 ? Ss 0:01 /sbin/init[root@ma ~]# ps -p 1 PID TTY ...

  8. 【Docker】Docker启动停止重启 Redirecting to /bin/systemctl start docker.service

    [root@liuawen local]# docker -v Docker version 1.13.1, build cccb291/1.13.1 [root@liuawen local]# 启动 ...

  9. 攻防世界 - Web(二)

    supersqli: (!!!) 1.判断有误注入,1'报错, 1' 报错,1'# 正常且为True,1' and 1=1# 正常且为True,1' and 1=2# 正常且为False,所以它里边存 ...

  10. Table controls and tabstrip controls

    本文转载自http://www.cnblogs.com/clsoho/archive/2010/01/21/1653268.html ONTROLS Syntax Forms Declaration ...