前置芝士
- 主席树
- STL-set
Description
你有 \(n\) 匹小马,分别标号 \(1 \sim n\),每个小马有三个属性 \(s_i, m_i, r_i\) 分别表示初始魔力,最大魔力,单位时间增加的魔力。然后有 \(m\) 次询问,每次询问给你三个值 \(t, l, r\) 表示在 \(t\) 时间吸取 \([l,r]\) 区间内的小马,输出每次吸取的魔力。
Solution
考虑颜色段均摊的做法,用一个 set 维护被更改的时间相同的颜色段。
由于只有区间赋值操作,且查询后立即赋值,所以每个颜色段只会对后面的一次查询产生影响。而且每次赋值只会增加 1 个颜色段,所以总的颜色段数量是 \(O(m)\) 级别的。
我们对整体建立两颗可持久化线段树(主席树),一棵在 \(\frac{m_i}{r_i}\) 处维护 \(r_i\),另一颗在 \(\frac{m_i}{r_i}\) 处维护 \(m_i\) 的和。
对于一个 \((t,l,r)\) 的询问,将 \([l,r]\) 通过 set 中的颜色段分成一段一段的区间,每个区间内被修改的时间都是相同的。
- 如果这个区间以前没被修改,说明有初值,我们暴力修改;
- 否则,设这个区间上次修改时间为 \(t_0\),此时初值已经没有影响了。当小马的 \(\frac{m_i}{r_i} < t - t_0\) 时,答案会加上 \(m_i\);否则答案会加上 \((t - t_0)r_i\)。这两种情况可以直接在主席树上查询。
对于 set 的细节,可以按照维护一个结构体,标记左右端点,是否是第一次覆盖和上一次覆盖的时间,然后维护整个区间,查询后就删掉,用新的区间覆盖。反正细节巨大多,具体看代码吧。
时间复杂度 \(O((n+m) \log n)\)。
Code
/*
Work by: Suzt_ilymics
Problem: 不知名屑题
Knowledge: 垃圾算法
Time: O(能过)
*/
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<queue>
#include<set>
#define int long long
#define orz cout<<"lkp AK IOI!"<<endl
using namespace std;
const int MAXN = 2e5+5;
const int INF = 1e9+7;
const int mod = 1e9+7;
const int Max = 1e6;
struct node {
int l, r, tag, tim;
bool operator < (const node &b) const { return l < b.l; }
};
int n, m, Ans = 0;
int fir[MAXN], lim[MAXN], r[MAXN];
int root_r[MAXN], root_m[MAXN];
set<node> s;
int read(){
int s = 0, f = 0;
char ch = getchar();
while(!isdigit(ch)) f |= (ch == '-'), ch = getchar();
while(isdigit(ch)) s = (s << 1) + (s << 3) + ch - '0' , ch = getchar();
return f ? -s : s;
}
namespace Hjt {
#define ls lson[now_]
#define rs rson[now_]
int lson[MAXN << 5], rson[MAXN << 5], siz[MAXN << 5], val[MAXN << 5], node_num = 0;
void Modify(int &now_, int pre_, int l, int r, int pos, int val_) {
now_ = ++ node_num;
lson[now_] = lson[pre_], rson[now_] = rson[pre_];
val[now_] = val[pre_] + val_;
if(l == r) return ;
int mid = (l + r) >> 1;
if(mid >= pos) Modify(ls, lson[pre_], l, mid, pos, val_);
else Modify(rs, rson[pre_], mid + 1, r, pos, val_);
}
int Query(int now_, int pre_, int l, int r, int L, int R) {
if(L <= l && r <= R) return val[now_] - val[pre_];
if(l == r) return 0;
int mid = (l + r) >> 1, ans = 0;
if(mid >= L) ans += Query(ls, lson[pre_], l, mid, L, R);
if(mid < R) ans += Query(rs, rson[pre_], mid + 1, r, L, R);
return ans;
}
}
int BL(const node &it, int t) {
int res = 0; t -= it.tim;
for(int i = it.l; i <= it.r; ++i) {
res += min(lim[i], fir[i] + r[i] * t);
fir[i] = 0;
}
return res;
}
int Qry(const node &it, int t) {
if(it.tag) return BL(it, t);
t -= it.tim;
return Hjt::Query(root_m[it.r], root_m[it.l - 1], 0, Max, 0, t - 1) + t * Hjt::Query(root_r[it.r], root_r[it.l - 1], 0, Max, t, Max);
}
signed main()
{
// freopen("CF453E.in","r",stdin);
// freopen("test.out","w",stdout);
n = read();
for(int i = 1; i <= n; ++i) {
fir[i] = read(), lim[i] = read(), r[i] = read();
if(!r[i]) {
Hjt::Modify(root_r[i], root_r[i - 1], 0, Max, 0, 0);
Hjt::Modify(root_m[i], root_m[i - 1], 0, Max, 0, 0);
} else {
Hjt::Modify(root_r[i], root_r[i - 1], 0, Max, lim[i] / r[i], r[i]);
Hjt::Modify(root_m[i], root_m[i - 1], 0, Max, lim[i] / r[i], lim[i]);
}
}
m = read();
s.insert((node){1, n, 1, 0});
for(int i = 1, t, l, r; i <= m; ++i) {
t = read(), l = read(), r = read(), Ans = 0;
set<node>::iterator it1 = --s.upper_bound((node){l, 0, 0, 0}),it2 = --s.upper_bound((node){r, 0, 0, 0});
if(it1 == it2) {
Ans += Qry((node){l, r, it1->tag, it1->tim}, t);
node is = *it1;
s.erase(it1);
if(l != is.l) s.insert((node){is.l, l - 1, is.tag, is.tim});
s.insert((node){l, r, 0, t});
if(r != is.r) s.insert((node){r + 1, is.r, is.tag, is.tim});
} else {
Ans += Qry((node){l, it1->r, it1->tag, it1->tim}, t);
node is1 = *it1, is2 = *it2;
for(set<node>::iterator it = s.erase(it1); it != it2 && it != s.end(); it = s.erase(it)) {
Ans += Qry(*it, t);
}
Ans += Qry((node){it2->l, r, it2->tag, it2->tim}, t);
s.erase(it2);
if(is1.l <= l - 1) s.insert((node){is1.l, l - 1, is1.tag, is1.tim});
if(is2.r >= r + 1) s.insert((node){r + 1, is2.r, is2.tag, is2.tim});
s.insert((node){l, r, 0, t});
}
printf("%lld\n", Ans);
}
return 0;
}