HDU 3872 解题报告

转移自一段连续的区间的最小值的可以确定要用线段树。关键点是如何构造这个线段树的问题。

用一个单调栈来维护这一结构,维护使队列单调递减所以 q[tail - 2] + 1 到q[tail - 1]的最大值是energy[q[tail - 1]]在线段树中插入这个段值,如果弹栈的话把这段值减去。

每次转移dp[i] = query(next[i] + 1, i, 1);求区间的最小值。

由于朴素的转移方程为dp[i] = dp[k] + minn[k + 1][i]

然后把dp[i]插入到i + 1节点中以为后面的转移做准备。

这个题还是很难想的,我的思维深度是没有到这里,只能想到求区间最值,却想不到用一个单调的栈维护区间,但是相比于那个上次的那个最短路还是简单一些的(个人感觉)。

代码如下:

HDU 3872
/*
* =====================================================================================
*
* Filename: dp.cpp
*
* Description: a hard dp with segment tree
*
* Version: 1.0
* Created: 2011年07月27日 10时37分59秒
* Revision: none
* Compiler: gcc
*
* Author: ronaflx
* Company: hit-ACM-Group
*
* =====================================================================================
*/

#include
<cstdlib>
#include
<cctype>
#include
<cstring>
#include
<cstdio>
#include
<cmath>
#include
<ctime>
#include
<climits>
#include
<algorithm>
#include
<functional>
#include
<numeric>
#include
<vector>
#include
<map>
#include
<set>
#include
<queue>
#include
<stack>
#include
<bitset>
#include
<list>
#include
<string>
#include
<iostream>
#include
<sstream>
#include
<fstream>
#include
<iomanip>
#include
<stdexcept>
#include
<utility>
#include
<cassert>
#include
<complex>
using namespace std;
#define LEFT(i) ((i) << 1)
#define RIGHT(i) (((i) << 1) | 1)
#define MID(i) ((l[i] + r[i]) >> 1)
#define CC(i, v) memset(i, v, sizeof(i))
#define REP(i, l, n) for(int i = l;i < int(n);++i)
#define FOREACH(con, i) for(__typeof(con.begin()) i = con.begin();i != con.end();++i)
typedef
long long LL;

const int N = 100001;
const LL INF = 1000000000;
class segmentTree
{
public:
LL minn[N
* 4], delta[N * 4];
int l[N * 4], r[N * 4];
void build(int ll, int rr, int n)
{
l[n]
= ll, r[n] = rr, minn[n] = 0, delta[n] = 0;
int mid = MID(n);
if(ll == rr) return;
build(ll, mid, LEFT(n));
build(mid
+ 1, rr, RIGHT(n));
}
void updateSon(int n, LL v)
{
minn[n]
+= v;
delta[n]
+= v;
}
void update(int n)
{
if(delta[n] == 0) return;
updateSon(LEFT(n), delta[n]);
updateSon(RIGHT(n), delta[n]);
delta[n]
= 0;

}
void insert(int ll, int rr, long long v, int n)
{
if(l[n] == ll && r[n] == rr)
{
updateSon(n, v);
return;
}
update(n);
int mid = MID(n);
if(rr <= mid)
insert(ll, rr, v, LEFT(n));
else if(mid < ll)
insert(ll, rr, v, RIGHT(n));
else
{
insert(ll, mid, v, LEFT(n));
insert(mid
+ 1, rr, v, RIGHT(n));
}
minn[n]
= min(minn[LEFT(n)], minn[RIGHT(n)]);
}
LL query(
int ll, int rr, int n)
{
if(l[n] == ll && r[n] == rr)
return minn[n];
int mid = MID(n);
update(n);
LL ans;
if(rr <= mid)
ans
= query(ll, rr, LEFT(n));
else if(mid < ll)
ans
= query(ll, rr, RIGHT(n));
else ans = min(query(ll, mid, LEFT(n)), query(mid + 1, rr, RIGHT(n)));
minn[n]
= min(minn[LEFT(n)], minn[RIGHT(n)]);
return ans;
}
}tree;
int type[N], next[N], q[N], last[N];
LL dp[N], energy[N]
= {INF};
int main()
{
int t, n;
scanf(
"%d", &t);
while(t--)
{
scanf(
"%d", &n);
for(int i = 1;i <= n;i++)
scanf(
"%d", &type[i]);
for(int i = 1;i <= n;i++)
scanf(
"%lld", &energy[i]);
CC(last,
0);
tree.build(
0, n + 1, 1);
for(int i = 1;i <= n;i++)
{
next[i]
= last[type[i]];
last[type[i]]
= i;
}
int tail = 0;
q[tail
++] = 0;
for(int i = 1;i <= n;i++)
{
while(tail && energy[q[tail - 1]] <= energy[i])
{
tree.insert(q[tail
- 2] + 1, q[tail - 1], -energy[q[tail - 1]], 1);
tail
--;
}
q[tail
++] = i;
tree.insert(q[tail
- 2] + 1, i, energy[i], 1);
dp[i]
= tree.query(next[i] + 1, i, 1);
tree.insert(i
+ 1, i + 1, dp[i], 1);
}
printf(
"%lld\n", dp[n]);
}
return 0;
}

  

ORZ 70教主,每次都能按时完成题目……祝你今年拿金牌!!

你可能感兴趣的:(HDU)