1. 程式人生 > 實用技巧 >hdu4719 Oh My Holy FFF 線段樹維護dp

hdu4719 Oh My Holy FFF 線段樹維護dp

題意:
給你一個長度為n的陣列v,你需要把這個陣列分成很多段,你需要保證每一段的長度不能超過k
我們設一共有m段,每一段右邊界那個數為bi
那麼我們要使得sum(bi*bi-b(i-1))最大  (1<=i<=m,b0=0)
你需要保證bi>b(i-1) (1<=i<=m)
sum():表示求和

題解:
我們設陣列下標從1開始
dp[i]表示:對於v陣列的前i個數的最大sum(bi*bi-b(i-1))為dp[i]
dp轉移方程:
dp[i]=dp[j]+v[i]*v[i]-v[j]  (i>j且v[i]>v[j])
dp轉移方程很容易找到,但是如果對於一個i,我們去尋找所有滿足條件的j的話就該TLE了

那麼我們可以使用線段樹進行維護,維護第i個位置的值為dp[i]-v[i]。這樣的話對於一個j(j>i)
我們只需要線上段樹的[1,j-1]區間查找出來最大的值就可以了
對於查找出來的值我們只需要加上v[j]*v[j]就是dp[j]的值(這一點很重要,可以說就是把維護的值改變了一下)

但是我們發現題目還要求v[j]>v[i],怎麼辦呢,我們可以對所有vi排序,按照排過序之後順序進行線段樹維護查詢更新
就可以了

程式碼:

/*
題意:
給你一個長度為n的陣列v,你需要把這個陣列分成很多段,你需要保證每一段的長度不能超過k
我們設一共有m段,每一段右邊界那個數為bi
那麼我們要使得sum(bi*bi-b(i-1))最大  (1<=i<=m,b0=0)
你需要保證bi>b(i-1) (1<=i<=m)
sum():表示求和

題解:
我們設陣列下標從1開始
dp[i]表示:對於v陣列的前i個數的最大sum(bi*bi-b(i-1))為dp[i]
dp轉移方程:
dp[i]=dp[j]+v[i]*v[i]-v[j]  (i>j且v[i]>v[j])
dp轉移方程很容易找到,但是如果對於一個i,我們去尋找所有滿足條件的j的話就該TLE了

那麼我們可以使用線段樹進行維護,維護第i個位置的值為dp[i]-v[i]。這樣的話對於一個j(j>i)
我們只需要線上段樹的[1,j-1]區間查找出來最大的值就可以了
對於查找出來的值我們只需要加上v[j]*v[j]就是dp[j]的值(這一點很重要,可以說就是把維護的值改變了一下)

但是我們發現題目還要求v[j]>v[i],怎麼辦呢,我們可以對所有vi排序,按照排過序之後順序進行線段樹維護查詢更新
就可以了
*/
#include <iostream> #include <cstdio> #include <cstring> #include <cstdlib> #include <algorithm> using namespace std; typedef long long ll; const int maxn=1e5+10; const int mod=20071027; const int INF=0x3f3f3f3f; ll tree[maxn<<2],dp[maxn]; ll max(ll a,ll b) { if(
a<b) return b; else return a; } void push_up(ll rt) { tree[rt]=max(tree[rt<<1],tree[rt<<1|1]); } void update(ll rt,ll L,ll R,ll pos,ll val) { if(L==R) { tree[rt]=val; return; } ll mid=(L+R)>>1; if(pos<=mid) update(rt<<1,L,mid,pos,val); else update(rt<<1|1,mid+1,R,pos,val); push_up(rt); } ll query(ll rt,ll L,ll R,ll LL,ll RR) { if(LL<=L && RR>=R) { return tree[rt]; } ll mid=(L+R)>>1,ans=-1; if(LL<=mid) ans=max(ans,query(rt<<1,L,mid,LL,RR)); if(RR>mid) ans=max(ans,query(rt<<1|1,mid+1,R,LL,RR)); return ans; } struct shudui { ll val,id; }v[maxn],w[maxn]; bool cmp(shudui x,shudui y) { return x.val<y.val; } int main() { ll t,p=0; scanf("%lld",&t); while(t--) { ll pos=0; memset(tree,-1,sizeof(tree)); memset(dp,-1,sizeof(dp)); ll n,k; scanf("%lld%lld",&n,&k); if(k==1) { for(ll i=1;i<=n;++i) scanf("%lld",&v[i].val),v[i].id=i; ll res=v[1].val*v[1].val,flag=0; for(ll i=2;i<=n;++i) { if(v[i].val>v[i-1].val) { res=(res+v[i].val*v[i].val)-v[i-1].val; } else { flag=1; break; } } if(flag==0) printf("Case #%lld: %lld\n",++p,res); else printf("Case #%lld: No solution\n",++p); continue; } for(ll i=2;i<=n+1;++i) scanf("%lld",&v[i].val),v[i].id=i; update(1,1,n,1,0); dp[1]=v[1].val*v[1].val; ll tmp=dp[1]-v[1].val; sort(v+2,v+2+n,cmp); //printf("%lld**************\n",query(1,1,n,3,4)); for(ll i=2;i<=n+1;++i) { if(pos>0 && v[i].val!=v[i-1].val) { //printf("%lld*******\n",pos); for(ll i=0;i<pos;++i) { update(1,1,n,w[i].id,w[i].val); } pos=0; } if(v[i].id==1) { w[0].id=1; w[0].val=tmp; pos++; continue; } ll ans=query(1,1,n,max(1,v[i].id-k),v[i].id-1); //printf("%lld %lld*****\n",ans,v[i].id); if(ans!=-1) { w[pos].id=v[i].id; w[pos].val=(ans+v[i].val*v[i].val)-v[i].val; dp[v[i].id]=max(w[pos].val+v[i].val,dp[v[i].id]); pos++; } } if(dp[n+1]!=-1) printf("Case #%lld: %lld\n",++p,dp[n+1]); else printf("Case #%lld: No solution\n",++p); } return 0; }