HDU 3954 Level up
链接:http://acm.split.hdu.edu.cn/showproblem.php?pid=3954
题意:给你n个英雄,现在来了很多波怪物,每波怪物的会带来经验使英雄升级,英雄的得到的经验为其等级*e;询问某段区间的最大经验,英雄升级后经验值不会变为0,是直接累积的;
思路1,一颗线段树维护:
因为英雄的等级k不超过10,那么每个英雄升级的次数不会超过10次,因此我们对,每次来的e判断会不会使这段区间的英雄升级,如果有暴力单点更新,没有lazy标记下 return ;关键在与确定区间的最小升级系数;
如果升级那么有 : max_k * e + max_exp >= level[max_k+1],-> e >= (level[max_k+1] - max_exp)/max_k ;
令mn_exp = (level[max_k+1] - max_exp)/max_k。 只要e >= mn_exp 那么区间就会有升级,所以维护下区间的最小 mn_exp 即可,注意最高级后level设为 inf .
#include<bits/stdc++.h>
#define LL long long
using namespace std;
const int maxn = 1e4 + 99;
int level[20], k;
struct segtree {
int l, r;
int max_exp, max_k;
int exp_lazy;
int rest;
}tr[maxn<<2];
void build(int l, int r, int pos)
{
tr[pos].l = l; tr[pos].r = r;
tr[pos].max_exp = 0; tr[pos].exp_lazy = 0;
tr[pos].rest = level[2];
tr[pos].max_k = 1;
if (l == r)return;
int mid = l + r >> 1;
build(l, mid, pos << 1);
build(mid + 1, r, pos << 1 | 1);
}
void push_up(int pos)
{
tr[pos].rest = min(tr[pos << 1].rest, tr[pos << 1 | 1].rest);//维护区间的最小系数
tr[pos].max_exp = max(tr[pos << 1].max_exp, tr[pos << 1 | 1].max_exp);
tr[pos].max_k = max(tr[pos << 1].max_k, tr[pos << 1 | 1].max_k);
}
void push_down(int pos)
{
if (tr[pos].exp_lazy == 0)return;
tr[pos<<1].max_exp += tr[pos<<1].max_k*tr[pos].exp_lazy;
tr[pos << 1|1].max_exp += tr[pos<<1|1].max_k*tr[pos].exp_lazy;//一定是叶子节点的k*父亲节点的lazy_exp;
tr[pos << 1].exp_lazy += tr[pos].exp_lazy;
tr[pos << 1 | 1].exp_lazy += tr[pos].exp_lazy;
tr[pos << 1].rest -= tr[pos].exp_lazy;
tr[pos << 1 | 1].rest -= tr[pos].exp_lazy;
tr[pos].exp_lazy = 0;
}
int find_k(int exp)
{
for (int i = k; i >= 2; --i)if (level[i] <= exp)return i;
return 1;
}
void update(int ll, int rr, int e, int pos)
{
if (ll == tr[pos].l&&tr[pos].r == rr)
{
if (tr[pos].rest > e)
{
tr[pos].max_exp += tr[pos].max_k*e;
tr[pos].rest -= e;
tr[pos].exp_lazy += e;
return;
}
if (ll == rr)
{
tr[pos].max_exp += e*tr[pos].max_k;
int rank = find_k(tr[pos].max_exp);
tr[pos].max_k = rank;
tr[pos].exp_lazy = 0;
tr[pos].rest = (level[tr[pos].max_k + 1] - tr[pos].max_exp) / tr[pos].max_k;
if (tr[pos].max_k == k) tr[pos].rest = 1 << 30;//这里一定要写,,,等级达到max后就不能升级,so,rest = INF;
return;
}
}
push_down(pos);
int mid = tr[pos].l + tr[pos].r >> 1;
if (mid >= rr) update(ll, rr, e, pos << 1);
else if (mid < ll)update(ll, rr, e, pos << 1 | 1);
else
{
update(ll, mid, e, pos << 1);
update(mid + 1, rr, e, pos << 1 | 1);
}
push_up(pos);
}
int ask_ans(int ll, int rr, int pos)
{
if (ll == tr[pos].l&&tr[pos].r == rr) return tr[pos].max_exp;
push_down(pos);
int mid = tr[pos].l + tr[pos].r >> 1;
if (mid >= rr) return ask_ans(ll, rr, pos << 1);
else if (mid < ll) return ask_ans(ll, rr, pos << 1 | 1);
else return max(ask_ans(ll, mid, pos << 1), ask_ans(mid + 1, rr, pos << 1 | 1));
}
int main()
{
int t, ks = 1;
scanf("%d", &t);
while (t--)
{
memset(level, 0, sizeof level);
printf("Case %d:\n", ks++);
int n, qw;
scanf("%d%d%d", &n, &k, &qw);
build(1, n, 1);
for (int i = 2; i <= k; ++i)scanf("%d", &level[i]);
while (qw--)
{
char que;
scanf(" %c ", &que);
if (que == 'Q')
{
int l, r;
scanf("%d%d", &l, &r);
printf("%d\n", ask_ans(l, r, 1));
}
else
{
int l, r, e;
scanf("%d%d%d", &l, &r, &e);
update(l, r, e, 1);
}
}
puts("");
}
return 0;
}
思路2 :
假如区间英雄的等级一样,那不就是个sb题吗。
由于 k <= 10, 我们可以对每个不同的等级建一颗线段树,没有英雄的位置值为 - inf,最初时,等级一的每个位置->0 。每次区间修改,对每颗线段数修改,再对每颗线段树维护最小升级的经验, 由于 k 相同,可以很简单的维护出来。如果区间有升级暴力更新,将现在对应得位置-> -inf ,再对应该升级到的那颗线段树暴力更新即可维护,注意,更新时,要倒着更新,至于为什么自己想想。查询时,对k可线段树取max即可。
#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e4 + 1010;
using ll = long long;
#define inf 0x7f7f7f7f
int mx[11][maxn << 2], lazy[11][maxn << 2];
int lev[100], n, q;
inline void maintain(int rt, int k)
{
mx[k][rt] = max(mx[k][rt << 1], mx[k][rt << 1 | 1]);
}
inline void push(int rt, int k)
{
if (lazy[k][rt] == 0) return;
mx[k][rt << 1] += lazy[k][rt];
mx[k][rt << 1 | 1] += lazy[k][rt];
lazy[k][rt << 1] += lazy[k][rt];
lazy[k][rt << 1 | 1] += lazy[k][rt];
lazy[k][rt] = 0;
}
void add(int l, int r, int rt, int k, int p, int v)
{
if (l == r) mx[k][rt] = v;
else
{
push(rt, k);
int mid = l + r >> 1;
if (p <= mid) add(l, mid, rt << 1, k, p, v);
else add(mid + 1, r, rt << 1 | 1, k, p, v);
maintain(rt, k);
}
}
void update(int l, int r, int rt, int k, int ql, int qr, int e)
{
if (mx[k][rt] < 0) return;
if (ql <= l && qr >= r)
{
if (e*k < lev[k + 1] - mx[k][rt] && mx[k][rt] >= 0)
{
mx[k][rt] += e * k;
lazy[k][rt] += e * k;
return;
}
if (l == r && mx[k][rt] >= 0)
{
int rk = k;
mx[k][rt] += k * e;
while (lev[rk + 1] <= mx[k][rt]) rk++;
add(1, n, 1, rk, l, mx[k][rt]);
mx[k][rt] = -inf;
return;
}
if (l == r)return;
}
push(rt, k);
int mid = l + r >> 1;
if (ql <= mid) update(l, mid, rt << 1, k, ql, qr, e);
if (qr > mid) update(mid + 1, r, rt << 1 | 1, k, ql, qr, e);
maintain(rt, k);
}
int query(int l, int r, int rt, int k, int ql, int qr)
{
if (ql <= l && qr >= r) return mx[k][rt];
else
{
push(rt, k);
int mid = l + r >> 1, ret = 0;
if (ql <= mid) ret = query(l, mid, rt << 1, k, ql, qr);
if (qr > mid) ret = max(ret, query(mid + 1, r, rt << 1 | 1, k, ql, qr));
return ret;
}
}
int main()
{
int k, t;
scanf("%d", &t);
while (t--)
{
int df;
scanf("%d %d %d", &n, &k, &q);
for (int i = 0;i < n * 4; ++i)
{
for (int j = 2;j <= k; ++j)
mx[j][i] = -inf, lazy[j][i] = 0;
mx[1][i] = lazy[1][i] = 0;
}
for (int i = 2;i <= k; ++i) scanf("%d", &lev[i]);
lev[k + 1] = inf;
static int ks = 0;
printf("Case %d:\n", ++ks);
while (q--)
{
char op[10];
scanf("%s", op);
int l, r, e;
if (op[0] == 'Q')
{
scanf("%d%d", &l, &r);
int ret = 0;
for (int i = 1;i <= k; ++i)
ret = max(ret, query(1, n, 1, i, l, r));
printf("%d\n", ret);
}
else
{
scanf("%d %d %d", &l, &r, &e);
for (int i = k;i >= 1; --i)
update(1, n, 1, i, l, r, e);
}
}
puts("");
}
return 0;
}