Energy stones
题意
-
每颗石头初始能量是 E [ i ] E[i] E[i], 每秒钟增加 L [ i ] L[i] L[i], 上限是 C [ i ] C[i] C[i]
-
区间收割 M M M次,求收割能量的总和
-
收割时候会把区间里的能量都变为0
思路
分割时间
我们将时间分为两种
令 d [ i ] = ( c [ i ] + l [ i ] − 1 ) / l [ i ] d[i] = (c[i] + l[i] - 1) / l[i] d[i]=(c[i]+l[i]−1)/l[i],小心除0
大于 d [ i ] d[i] d[i]的时间段数乘 c [ i ] c[i] c[i]
小于 d [ i ] d[i] d[i]的时间段数 乘 时间 乘 l [ i ] l[i] l[i]
树状数组维护单点的时间段
首先我们用树状数组维护一个点的时间段信息
一个树状数组维护时间段的个数,单点表示时长为 i i i的时间段个数有几个
q u e r y ( d [ i ] , m a x n ) ∗ c [ i ] query(d[i], maxn)* c[i] query(d[i],maxn)∗c[i]即为到达上限的答案
一个树状数组维护时间段和时间的乘积,单点表示时长为 i i i的时间段个数乘时间长度
q u e r y ( 1 , d [ i ] − 1 ) ∗ l [ i ] query(1, d[i] - 1)* l[i] query(1,d[i]−1)∗l[i]即为未达到上限的答案
区间修改
由于修改区间状态, l [ i ] − r [ i ] l[i] - r[i] l[i]−r[i]状态更改
那么我们在 l [ i ] l[i] l[i]更改状态,在 r [ i ] r[i] r[i]改回来即可
- 用 s e t set set维护覆盖该点的收割区间,记录所有覆盖该点的收割时间
l [ i ] l[i] l[i]时加入收割时间, r [ i ] r[i] r[i]删除收割时间
- 每加入一个新的收割时间
可能存在
t
2
t2
t2插入t1,t3
中的情况
(
t
1
<
t
2
<
t
3
)
(t1 < t2 < t3)
(t1<t2<t3)
原本已经计算了 t 3 − t 1 t3-t1 t3−t1的时间段,将 t 3 − t 1 t3-t1 t3−t1删除
将 t 2 − t 1 t2-t1 t2−t1和 t 3 − t 2 t3-t2 t3−t2加入,反之亦然
- 第一个收割时间即为初始能量增加的情况,特殊处理一下
代码
树状数组
namespace Tree {
const int maxn = 200005;
const int limit = 200000;
int tree[maxn];
inline int lowbit(int x) {
return x & -x;
}
void update(int x, int val) { //维护时间段个数
for (int i = x; i <= limit; i += lowbit(i)) tree[i] += val;
}
int query(int left, int right) {
int res = 0;
for (int i = right; i; i -= lowbit(i)) res += tree[i];
for (int i = left - 1; i; i -= lowbit(i)) res -= tree[i];
return res;
}
LL num_tree[maxn];
void num_update(int x, int val) { //维护时间段*时间总和
for (int i = x; i <= limit; i += lowbit(i)) num_tree[i] += LL(x) * val;
}
LL num_query(int left, int right) {
LL res = 0;
for (int i = right; i; i -= lowbit(i)) res += num_tree[i];
for (int i = left - 1; i; i -= lowbit(i)) res -= num_tree[i];
return res;
}
}
区间维护
for (int i = 1; i <= n; i++) {
for (auto it : S[i]) { //加入区间开始
T.insert(it);
auto iter = T.find(it);
auto pre = iter, suf = iter;
if (iter != T.begin()) //加入t2-t1
Tree::update(*iter - *--pre, 1),
Tree::num_update(*iter - *pre, 1);
if (++suf != T.end()) //加入t3-t2
Tree::update(*suf - *iter, 1),
Tree::num_update(*suf - *iter, 1);
if (iter != T.begin() && suf != T.end()) //删除t3-t1
Tree::update(*suf - *pre, -1),
Tree::num_update(*suf - *pre, -1);
}
if (T.size()) {
auto iter = T.begin(); //第一个时间点为a[i]+t*l[i]的情况
if (a[i] + LL(*iter) * l[i] >= c[i]) ans += LL(c[i]);
else ans += LL(a[i]) + LL(*iter) * l[i];
if (l[i]) //计算答案
ans += LL(Tree::query(d[i], 200000)) * c[i],
ans += LL(Tree::num_query(1, d[i] - 1)) * l[i];
}
for (auto it : E[i]) {
auto iter = T.find(it);
auto pre = iter, suf = iter;
if (iter != T.begin()) //删除t2-t1
Tree::update(*iter - *--pre, -1),
Tree::num_update(*iter - *pre, -1);
if (++suf != T.end()) //删除t3-t2
Tree::update(*suf - *iter, -1),
Tree::num_update(*suf - *iter, -1);
if (iter != T.begin() && suf != T.end()) //加入t3-t1
Tree::update(*suf - *pre, 1),
Tree::num_update(*suf - *pre, 1);
T.erase(it);
}
}
AC
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <set>
#include <vector>
typedef long long LL;
using namespace std;
namespace Tree {
const int maxn = 200005;
const int limit = 200000;
int tree[maxn];
inline int lowbit(int x) {
return x & -x;
}
void update(int x, int val) {
for (int i = x; i <= limit; i += lowbit(i)) tree[i] += val;
}
int query(int left, int right) {
int res = 0;
for (int i = right; i; i -= lowbit(i)) res += tree[i];
for (int i = left - 1; i; i -= lowbit(i)) res -= tree[i];
return res;
}
LL num_tree[maxn];
void num_update(int x, int val) {
for (int i = x; i <= limit; i += lowbit(i)) num_tree[i] += LL(x) * val;
}
LL num_query(int left, int right) {
LL res = 0;
for (int i = right; i; i -= lowbit(i)) res += num_tree[i];
for (int i = left - 1; i; i -= lowbit(i)) res -= num_tree[i];
return res;
}
}
const int maxn = 100005;
set<int> T;
int a[maxn], l[maxn], c[maxn], d[maxn];
int t[maxn], s[maxn], e[maxn];
vector<int> S[maxn], E[maxn];
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
cout.tie(nullptr);
int __case, n, m; cin >> __case;
for (int i = 1; i <= __case; i++) {
cin >> n; T.clear();
for (int i = 1; i <= n; i++) {
cin >> a[i] >> l[i] >> c[i];
if (l[i])d[i] = (c[i] + l[i] - 1) / l[i];
S[i].clear(); E[i].clear();
}
cin >> m;
for (int i = 1; i <= m; i++) {
cin >> t[i] >> s[i] >> e[i];
S[s[i]].push_back(t[i]);
E[e[i]].push_back(t[i]);
}
LL ans = 0;
for (int i = 1; i <= n; i++) {
for (auto it : S[i]) {
T.insert(it);
auto iter = T.find(it);
auto pre = iter, suf = iter;
if (iter != T.begin())
Tree::update(*iter - *--pre, 1),
Tree::num_update(*iter - *pre, 1);
if (++suf != T.end())
Tree::update(*suf - *iter, 1),
Tree::num_update(*suf - *iter, 1);
if (iter != T.begin() && suf != T.end())
Tree::update(*suf - *pre, -1),
Tree::num_update(*suf - *pre, -1);
}
if (T.size()) {
auto iter = T.begin();
if (a[i] + LL(*iter) * l[i] >= c[i]) ans += LL(c[i]);
else ans += LL(a[i]) + LL(*iter) * l[i];
if (l[i])
ans += LL(Tree::query(d[i], 200000)) * c[i],
ans += LL(Tree::num_query(1, d[i] - 1)) * l[i];
}
for (auto it : E[i]) {
auto iter = T.find(it);
auto pre = iter, suf = iter;
if (iter != T.begin())
Tree::update(*iter - *--pre, -1),
Tree::num_update(*iter - *pre, -1);
if (++suf != T.end())
Tree::update(*suf - *iter, -1),
Tree::num_update(*suf - *iter, -1);
if (iter != T.begin() && suf != T.end())
Tree::update(*suf - *pre, 1),
Tree::num_update(*suf - *pre, 1);
T.erase(it);
}
}
cout << "Case #" << i << ": " << ans << '\n';
}
}