/* ** 日期: 2013-9-12 ** 题目大意:有n个数,划分为多个部分,假设M份,每份不能多于L个。每个数有一个h[i], ** 每份最右边的那个数要大于前一份最右边的那个数。设每份最右边的数为b[i], ** 求最大的sum{b[i]² - b[i - 1]},1≤i≤M,其中b[0] = 0。 ** 思路:朴素DP为,dp[i]表示以i为结尾的最大划分。那么dp[i] = max{dp[j] - h[j] + h[i]²}, ** 1≤i-j≤L,h[j]<h[i]。这种会超时,采取线段树优化。因为有两个限制,考虑到若h[j]≥h[i], ** 那么求i的时候一定不会用到j,那么先按h排序再DP(h相同的,i大的排前面)。 ** __int64 没改完,导致wa了无数次 */ #include <iostream> #include <cstdio> #include <cstring> #include <algorithm> #define lson rt<<1,left,m #define rson rt<<1|1,m+1,right #define mid (left+right)>>1 using namespace std; typedef __int64 LL ; const int maxn = 100005; LL maxt[maxn<<2]; int L,n; struct node{ int pos;LL h; node(int id = 0,LL hight = 0):pos(id),h(hight){} bool operator < (node a)const{ if( h == a.h)return pos > a.pos; return h < a.h; } }arr[maxn]; void update(int rt,int left,int right,int place,LL val){ if( left == right){ maxt[rt] = val; return ; } int m = mid; if( place <= m)update(lson,place,val); else update(rson,place,val); maxt[rt] = max(maxt[rt<<1],maxt[rt<<1|1]); } LL query(int rt,int left,int right,int l,int r){ if( left >= l && right <= r){ return maxt[rt]; } int m = mid; LL res = -1; if( l <= m)res = max(res,query(lson,l,r)); if( r > m )res = max(res,query(rson,l,r)); return res; } LL solve(){ sort(arr,arr+n); LL res = -1; memset(maxt,-1,sizeof(maxt)); update(1,0,n,0,0); for(int i = 0; i < n; i++){ LL tmp = query(1,0,n,max(arr[i].pos-L,0),arr[i].pos - 1); if( tmp == -1 ){ if( arr[i].pos == n)return -1; continue; } res = tmp + (LL)arr[i].h*arr[i].h; if( arr[i].pos == n)return res; update(1,0,n,arr[i].pos,res - arr[i].h); } return res; } int main(){ int t,cas = 1;LL h; scanf("%d",&t); while( t-- ){ scanf("%d%d",&n,&L); for(int i = 0; i < n; i++){ scanf("%I64d",&h); arr[i] = node(i+1,h); } LL ans = solve(); printf("Case #%d: ",cas++); if( ans == -1)puts("No solution"); else printf("%I64d ",ans); } return 0; }