source: http://acm.hdu.edu.cn/showproblem.php?pid=3872
title: Dragon Ball
解法:线段树
这题的题解不太好写。需要利用的一个特点是:
设当前扫描到点i,vMax[u]表示[u, i]之间的能量的最大值,则vMax具有
不增性。
#include <iostream>
#include <stdio.h>
using namespace std;
typedef long long ll;
const int N = 100005;
const ll INF = 0X7FFFFFFFFFFFFFFFLL;
struct seg{
int l, r;
int dv;
ll best;
int mid(){
return (l + r) >> 1;
}
}segs[N<<2];
int v[N];
int last[N], r[N], vMax[N];
int n;
template <typename T>
void getNum(T& a){
a = 0;
char ch;
while(true){
ch = getchar();
if(ch >= '0' && ch <= '9') break;
}
a = ch - '0';
while(true){
ch = getchar();
if(ch < '0' || ch > '9') break;
a = a * 10 + ch - '0';
}
}
inline int ls(int id){
return (id << 1) + 1;
}
inline int rs(int id){
return (id << 1) + 2;
}
void build(int id, int l, int r){
segs[id].l = l; segs[id].r = r;
segs[id].dv = 0; segs[id].best = INF;
if(l < r){
int mid = segs[id].mid();
build(ls(id), l, mid);
build(rs(id), mid+1, r);
}
}
bool input(){
getNum(n);
int i, tMax, t;
tMax = 0;
for(i = 1; i <= n; i++){
getNum(t);
if(t > tMax){
do{
last[++tMax] = -1;
}while(tMax < t);
}
if(last[t] == -1){
r[i] = 1;
last[t] = i;
}else{
r[i] = last[t] + 1;
last[t] = i;
}
}
for(i = 1; i <= n; i++){
getNum(v[i]);
}
return true;
}
inline void up(int id, int dv){
segs[id].best += dv;
segs[id].dv += dv;
}
void down(int id){
if(segs[id].dv != 0){
up(ls(id), segs[id].dv);
up(rs(id), segs[id].dv);
segs[id].dv = 0;
}
}
void update(int id){
segs[id].best = INF;
if(segs[id].best > segs[ls(id)].best){
segs[id].best = segs[ls(id)].best;
}
if(segs[id].best > segs[rs(id)].best){
segs[id].best = segs[rs(id)].best;
}
}
void ins(int id, int pos, ll v){
if(segs[id].l == segs[id].r){
segs[id].best = v;
return ;
}
down(id);
if(pos <= segs[id].mid()){
ins(ls(id), pos, v);
}else{
ins(rs(id), pos, v);
}
update(id);
}
void ins(int id, int l, int r, ll dv){
if(l <= segs[id].l && segs[id].r <= r){
up(id, dv);
return;
}
if(segs[id].l == segs[id].r) return;
down(id);
int mid = segs[id].mid();
if(l <= mid){
ins(ls(id), l, r, dv);
}
if(r > mid){
ins(rs(id), l, r, dv);
}
update(id);
}
ll qry(int id, int l, int r){
if(l <= segs[id].l && segs[id].r <= r){
return segs[id].best;
}
if(segs[id].l == segs[id].r) return INF;
down(id);
int mid = segs[id].mid();
ll ans = INF;
if(l <= mid){
ans = min(ans, qry(ls(id), l, r));
}
if(r > mid){
ans = min(ans, qry(rs(id), l, r));
}
update(id);
return ans;
}
void solve(){
build(0, 1, n);
int i, u;
ll lv;
lv = 0;
for(i = 1; i <= n; i++){
ins(0, i, lv + v[i]);
last[i] = i;
vMax[i] = v[i];
for(u = i; u >= 1 && vMax[u] <= v[i]; ){
if(vMax[u] != v[i]){
ins(0, last[u], u, v[i] - vMax[u]);
}
last[i] = last[u];
u = last[u] - 1;
}
lv = qry(0, r[i], i);
}
printf("%I64d\n", lv);
}
int main() {
int t;
getNum(t);
while(t--){
input();
solve();
}
return 0;
}