【HDU4719】Oh My Holy FFF
这题面还挺有意思的
题目大意
有 n n n个士兵排成一行,现在要在不打乱顺序的前提下把他们分成若干组,每组士兵数量不能大于 L L L,且每一组最右边的士兵要高于前一组最右边的士兵。每种分组方案都有一个相应的分数,分数的计算方式为:从第二组开始, s c o r e = ∑ ( h i 2 − h i − 1 ) score=\sum(h_i^2-h_{i-1}) score=∑(hi2−hi−1)。其中 h i h_i hi表示第 i i i组最右边的士兵的身高。求最高的分数。
题解
前置知识:线段树优化DP
设
f
i
f_i
fi表示将前
i
i
i个士兵分成若干组所能获得的最大分数。则转移式为:
f i = max ( f j + h i 2 − h j ) f_i=\max(f_j+h_i^2-h_j) fi=max(fj+hi2−hj),其中 i − l e n ≤ j ≤ i − 1 i-len\leq j\leq i-1 i−len≤j≤i−1,且 h j < h i h_j<h_i hj<hi
将 h i 2 h_i^2 hi2提出,得 f i = h i 2 + max ( f j − h j ) f_i=h_i^2+\max(f_j-h_j) fi=hi2+max(fj−hj)。
于是,我们可以用线段树来维护 f j − h j f_j-h_j fj−hj的最大值。但是,怎么保证 h j < h i h_j<h_i hj<hi呢?
我们可以调整枚举
i
i
i的顺序,按
h
i
h_i
hi从小到大来枚举
i
i
i。如果两个士兵
i
i
i值相同,则先处理编号大的,避免被
h
h
h值相同的更新。求
f
i
f_i
fi时,在线线段树上查找对应段的最大值。求出
f
i
f_i
fi后,更新线段树上相应位置的值。因为是按
h
i
h_i
hi从小到大枚举的,所以不会存在
h
h
h值大的更新
h
h
h值小的。
总时间复杂度为
O
(
n
l
o
n
g
n
)
O(nlongn)
O(nlongn)
code
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#define lc k<<1
#define rc k<<1|1
using namespace std;
int t,n,l;
long long sum,inf=1e18,f[100005],tr[400005];
struct node{
long long x;
int id;
}a[100005];
bool cmp(node ax,node bx){
if(ax.x==bx.x) return ax.id>bx.id;
return ax.x<bx.x;
}
void build(int k,int l,int r){
tr[k]=-inf;
if(l==r) return;
int mid=(l+r)/2;
build(lc,l,mid);
build(rc,mid+1,r);
}
void ch(int k,int l,int r,int x,long long y){
if(l==r&&l==x){
tr[k]=y;
return;
}
if(r<x||l>x) return;
if(l==r) return;
int mid=(l+r)/2;
if(x<=mid) ch(lc,l,mid,x,y);
else ch(rc,mid+1,r,x,y);
tr[k]=max(tr[lc],tr[rc]);
}
void find(int k,int l,int r,int x,int y){
if(x>y) return;
if(l>=x&&r<=y){
sum=max(sum,tr[k]);
return;
}
if(r<x||l>y) return;
if(l==r) return;
int mid=(l+r)/2;
if(x<=mid) find(lc,l,mid,x,y);
if(y>mid) find(rc,mid+1,r,x,y);
}
int main()
{
scanf("%d",&t);
for(int o=1;o<=t;o++){
scanf("%d%d",&n,&l);
for(int i=1;i<=n;i++){
scanf("%lld",&a[i].x);
a[i].id=i;
f[i]=-inf;
}
build(1,1,n);
sort(a+1,a+n+1,cmp);
for(int i=1;i<=n;i++){
if(a[i].id<=l) f[i]=a[i].x*a[i].x;
sum=-inf;
find(1,1,n,max(a[i].id-l,1),a[i].id-1);
if(sum>-inf) f[i]=max(f[i],sum+a[i].x*a[i].x);
if(f[i]>-inf) ch(1,1,n,a[i].id,f[i]-a[i].x);
if(a[i].id==n){
if(f[i]==-inf) printf("Case #%d: No solution\n",o);
else printf("Case #%d: %lld\n",o,f[i]);
break;
}
}
}
return 0;
}