HDU4719-Oh My Holy FFF(DP线段树优化)

Oh My Holy FFF

Time Limit: 5000/2500 MS (Java/Others)    Memory Limit: 65535/65535 K (Java/Others)

Total Submission(s): 606    Accepted Submission(s): 141

Problem Description

N soldiers from the famous "*FFF* army" is standing in a line, from left to right.

 o   o   o   o   o   o   o   o   o   o   o   o   o   o   o   o   o   o
/F\ /F\ /F\ /F\ /F\ /F\ /F\ /F\ /F\ /F\ /F\ /F\ /F\ /F\ /F\ /F\ /F\ /F/ \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \

You, as the captain of *FFF*, want to divide them into smaller groups, but each group should still be continous in the original line. Like this:

 o   o   o  |  o   o   o   o  |  o   o   o   o   o   o  |  o   o   o   o   o
/F\ /F\ /F\ | /F\ /F\ /F\ /F\ | /F\ /F\ /F\ /F\ /F\ /F\ | /F\ /F\ /F\ /F\ /F/ \ / \ / \ | / \ / \ / \ / \ | / \ / \ / \ / \ / \ / \ | / \ / \ / \ / \ / \

In your opinion, the number of soldiers in each group should be no more than L.

Meanwhile, you want your division be "holy". Since the soldier may have different heights, you decide that for each group except the first one, its last soldier(which is the rightmost one) should be strictly taller than the previous group‘s last soldier. That
is, if we set bi as the height of the last soldier in group i. Then for i >= 2, there should be bi > bi-1.

You give your division a score, which is calculated as , b0 = 0 and 1 <= k <= M, if there are M groups in total. Note that M can equal to 1.

Given the heights of all soldiers, please tell us the best score you can get, or declare the division as impossible.

Input

The first line has a number T (T <= 10) , indicating the number of test cases.

For each test case, first line has two numbers N and L (1 <= L <= N <= 105), as described above.

Then comes a single line with N numbers, from H1 to Hn, they are the height of each soldier in the line, from left to right. (1 <= Hi <= 105)

Output

For test case X, output "Case #X: " first, then output the best score.

Sample Input

2
5 2
1 4 3 2 5
5 2
5 4 3 2 1

Sample Output

Case #1: 31
Case #2: No solution

题意:n(n < 1e5)个人排成一行,把它切成若干堆。要求每一堆的长度不超过l(l < 1e5),而且每一堆的最右一个人的身高都要比前一堆的最右一个人的身高要高,对于每一种方案,它的分数是SUM(b[k]^2-b[k-1] )  b[k] 为第k堆最右一个人的身高 要求最高的分数。

思路:朴素的DP 是  DP[i]  = max(DP[j] - b[j]) + b[i]*b[i]  ( i-l <=  j <= i-1 )  可是这样会超时(O(n^2)) 能够发现每次求DP[i] 的时候 实际就是求 区间[i-l,i-1]  DP[j]-b[j]的最大值,因此能够利用线段树优化。此时还须要解决一个问题:就是怎样保证每次求DP[i]的时候保证区间[i-l,i-1]
的每一个人的身高都是比自己矮的?  能够进行先排序。让矮的人先选,假设身高一样就让序号在后的先选,这样就不会有冲突了(单点更新的时候)。 每次查询的时候单点更新就可以。

#include <iostream>
#include <cstdio>
#include <cstring>
#include <vector>
#include <algorithm>
using namespace std;
#define REP(_,a,b)  for(int _=(a); _<=(b);++_)
#define sz(s) (int)((s).size())
typedef long long ll;
const int maxn = 1e5+10;
int n,l;
ll dp[maxn];
struct Num{
	ll h;
	int idx;
	Num(ll h = 0,int idx = 0):h(h),idx(idx){}
	friend bool operator < (Num a,Num b){
		if(a.h!=b.h) return a.h < b.h;
		else return a.idx > b.idx;
	}
};
vector<Num> vN;
struct node{
	int lson,rson;
	ll maxx;
	int mid(){
		return (lson+rson)>>1;
	}
}tree[maxn*4];
void pushUp(int rt){
	tree[rt].maxx = max(tree[rt<<1].maxx,tree[rt<<1|1].maxx);
} 

void build(int L,int R,int rt){
	tree[rt].lson = L;
	tree[rt].rson = R;
	tree[rt].maxx = -1;
	if(L==R){
		return;
	}
	int mid = tree[rt].mid();
	build(L,mid,rt<<1);
	build(mid+1,R,rt<<1|1);
}
void init(){
	vN.clear();
	memset(dp,-1,sizeof dp);
}
void update(int pos,int l,int r,int rt,ll x){
	if(l==r){
		tree[rt].maxx = x;
		return;
	}
	int mid = tree[rt].mid();
	if(pos<=mid){
		update(pos,l,mid,rt<<1,x);
	}else{
		update(pos,mid+1,r,rt<<1|1,x);
	}
	pushUp(rt);
}
ll query(int L,int R,int l,int r,int rt){
	if(L <=l && R >= r){
		return tree[rt].maxx;
	}
	int mid = tree[rt].mid();
	ll ret;
	bool flag = false;
	if(L <= mid){
		ret = query(L,R,l,mid,rt<<1);
		flag = true;
	}
	if(R > mid){
		if(flag){
			ret = max(ret,query(L,R,mid+1,r,rt<<1|1));
		}else{
			ret = query(L,R,mid+1,r,rt<<1|1);
		}
	}
	return ret;
}
void input(){
	scanf("%d%d",&n,&l);
	REP(_,1,n){
		ll h;
		scanf("%I64d",&h);
		vN.push_back(Num(h,_));
	}
	sort(vN.begin(),vN.end());
	build(0,n,1);
}
void solve(){
	update(0,0,n,1,0);
	REP(_,0,sz(vN)-1) {
		int ni = vN[_].idx;
		ll nh = vN[_].h;
		ll tm = query(max(ni-l,0),ni-1,0,n,1);
		if(tm>=0){
			dp[ni] = tm+nh*nh;
			update(ni,0,n,1,dp[ni]-nh);
		}
		if(ni==n) break;
	}
	if(dp[n]<=0){
		printf("No solution\n");
	}else{
		printf("%I64d\n",dp[n]);
	}
}
int main(){
	int ncase,T=1;
	cin >> ncase;
	while(ncase--){
		init();
		input();
		printf("Case #%d: ",T++);
		solve();
	}
	return 0;
} 

时间: 2024-12-16 08:17:20

HDU4719-Oh My Holy FFF(DP线段树优化)的相关文章

hdu 4719 Oh My Holy FFF(dp线段树优化)

Oh My Holy FFF Time Limit: 5000/2500 MS (Java/Others)    Memory Limit: 65535/65535 K (Java/Others) Total Submission(s): 848    Accepted Submission(s): 219 Problem Description N soldiers from the famous "*FFF* army" is standing in a line, from le

题解 HDU 3698 Let the light guide us Dp + 线段树优化

http://acm.hdu.edu.cn/showproblem.php?pid=3698 Let the light guide us Time Limit: 5000/2000 MS (Java/Others)    Memory Limit: 62768/32768 K (Java/Others) Total Submission(s): 759    Accepted Submission(s): 253 Problem Description Plain of despair was

hdu3698 Let the light guide us dp+线段树优化

http://acm.hdu.edu.cn/showproblem.php?pid=3698 Let the light guide us Time Limit: 5000/2000 MS (Java/Others)    Memory Limit: 62768/32768 K (Java/Others) Total Submission(s): 821    Accepted Submission(s): 285 Problem Description Plain of despair was

【uva1502/hdu4117-GRE Words】DP+线段树优化+AC自动机

这题我的代码在hdu上AC,在uva上WA. 题意:按顺序输入n个串以及它的权值di,要求在其中选取一些串,前一个必须是后一个的子串.问d值的和最大是多少. (1≤n≤2×10^4 ,串的总长度<=3*10^5) 题解: 这题一开始我的方向就错了,想了很久d[x][y]表示在AC自动机上的节点x.下一个串要大于y的dp.然而这样做数组要10^4*10^5=10^9级别,开都开不了,妥妥超时. 后来看了一眼题解...觉得自己智商真是感人... 用f[i]表示以第i个串为结尾的时候最大的d值,这样做

ZOJ 3650(多米诺骨牌 dp + 线段树优化)

题目链接:http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemCode=3650 题意: 给你n个骨牌,每个骨牌有一个在x轴上的位置和高度,每个骨牌可以想左推也可以向右推,问最少多少步可以把骨牌全部推倒. 思路: 之前Goodbye 2014 上的E题就是一道多米诺骨牌的题目,虽然跟这道题目不太一样但是还是提供了一下思路, 附题解的链接: http://blog.csdn.net/u013649253/article/details/4

hdu 3450 离散化+dp+线段树优化

链接:http://acm.hdu.edu.cn/showproblem.php?pid=3450 题意: 给你一串长度为n的序列,给一个d,要求找出有几个子序列能够满足两个相邻的元素之间差值不超过d. 思路: dp.定义dp[i]表示以第i个为结束的满足条件的子序列的个数. 转移方程:dp[i]=(∑i?1j=1dp[j])+1(abs(num[i]?num[j])<=d) 答案就是dp数组的总和最后扣掉n就可以了. 此时会发现更新的时间复杂度是O(n2),这个显然是过不了的. 转移的复杂度是

[后缀数组+dp/AC自动机+dp+线段树] hdu 4117 GRE Words

题意: 给你N个字符串, N(1 <= N <= 2w), 所有串的长度加一起不超过30w.每个串有个值.这个值[-1000, 1000]. 问不打乱字符串顺序,从中取若干个字符串,使得前一个串是后一个串的子串,求满足前面调条件的字符串值得和最大,求这个值. 思路: 其实就是一个很明显的dp. dp[i]代表以第i个字符串结尾的最大权值. 但是就是子串这个问题怎么处理. 由于这题数据比较水可以用后缀数组处理这个问题. 将所有字符串拼接,做sa. 每次在height数组里往上和往下寻找公共前缀等

[Poi2010]Monotonicity 2 (线段树优化DP)

题目描述 给出N个正整数a[1..N],再给出K个关系符号(>.<或=)s[1..k].选出一个长度为L的子序列(不要求连续),要求这个子序列的第i项和第i+1项的的大小关系为s[(i-1)mod K+1].求出L的最大值. 输入 第一行两个正整数,分别表示N和K (N, K <= 500,000).第二行给出N个正整数,第i个正整数表示a[i] (a[i] <= 10^6).第三行给出K个空格隔开关系符号(>.<或=),第i个表示s[i]. 输出 一个正整数,表示L的

poj 1769 Minimizing maximizer(dp+线段树)

题意:数列长度为n,m次操作(n<=50000,m<=500000),每次操作将区间[si,ti]从小到大排序,求至少使用几次操作使数列的最后一个数与经过所有操作后相等: 思路:选取最少的操作得到最优解,一般采用dp; 假设原数列的第1个数为最大值,dp[j]表示最大值移动到第j个位置需要至少的操作数: 初始令dp[1]=0,dp[j]=inf(j>1); 对于每个i,有dp[ti]=min(dp[ti],min(dp[j](si<=j<=ti))+1); 若复杂度为O(nm