lanxisi帶領著他的拆遷隊來整治乙個街道。這個街道由n個舊房子組成,從左到右編號為1..n。每個舊房子i有乙個正整數的美觀度ai。
lanxisi希望整個街道從左到右美觀度嚴格遞增,也就是保證ai
(i。但是舊的街道明顯不符合這個要求,於是lanxisi希望拆遷一些舊房子並在原地建立新房子來滿足這一要求。但是與很多拆遷隊一樣,很多釘子戶拒絕拆遷。所以lanxisi希望能保留最多的舊房子來滿足某些釘子戶,當然,保留乙個舊房子需要給房子主人bi的賠償金。最後,總花費=整治好以後所有房子美觀度之和+賠償金之和。新的美觀值也都必須是正整數。
現在,請你求出lanxisi最多能保留多少舊房子和整治這個街道所需要的最少總花費(當然是在保留舊房子最多這個前提下的)。
1<=n<=100000,0<=ai,bi<=100000000。
先思考最樸素的o(n^2)dp是如何運作的。fi
是以i結尾最長不下降子串行的長度。 si
是以i結尾的最小總花費。fi
=max
(fi+
1)ja[j]
i],a
[j]−
j<=a[
i]−i
) si
=min
(sj+
…)(j
a[j]
i],a
[j]−
j<=a[
i]−i
,f[i
]=f[
j]+1
) 如果有:
ja[j]
−j<=a[
k]−k
那麼一定有: a[
j]k]
所以可以去掉乙個條件。
這樣就可以可以用o(n log n)的時間按照a[i]-i為關鍵字dp出f。
接著按f分層dp。
打省略號的部分可以化成三部分:
1.和j有關的部分
2.和i有關的部分
3.(a[j]-j)*i
在同一層中,如果序號j遞增,則a[j]-j遞減,不然它們就不會在同一層。
所以滿足斜率優化形式。
但是並沒有這麼簡單。
注意條件除了a[j]-j<=a[i]-i,還有
j<
i 。
在分層後,序號並不是遞增,高層的序號可能比低層小,而且a[j]-j同樣只是在同一層內有序,不同層的大小關係無法判斷。
簡化問題後可以看作對一層的連續的一段j求 mi
n(sj
+(a[
j]−j
)∗i)
到這裡可以同線段樹的分治思想來拆分詢問。
對同一層建個線段樹,每個點存這個點所代表的區間的j的單調棧。
因為線段樹只有lo
g 層,每層最多n個,所以總共是o(
nlog
n)。接著把詢問利用線段樹拆成lo
g 段,每一段二分又是lo
g ,總複雜度是o(
nlog
2n) 。
code:
#include
#include
#define ll long long
#define ld long double
#define fo(i, x, y) for(ll i = x; i <= y; i ++)
#define fd(i, x, y) for(ll i = x; i >= y; i --)
#define min(a, b) ((a) < (b) ? (a) : (b))
using namespace std;
const ll n = 1e5 + 5;
ll n, a[n], b[n], f[n], l[n], ml;
ll s[n], g[n], h[n];
ll final[n], next[n], to[n], tot;
ll st, en, ans;
ld ji(ll k, ll b, ll l, ll r)
ld jj(ll x, ll y)
int d[n * 30], d0, z[n];
struct tree t[n * 4];
void build(int i, int x, int y)
d0 = t[i].r;
if(x == y) return;
int m = x + y >> 1;
build(i + i, x, m); build(i + i + 1, m + 1, y);
}ll xx;
ll find(int i, int x, int y, int l, int r)
return h[d[as]] * xx + g[d[as]];
}int m = x + y >> 1;
if(r <= m) return find(i + i, x, m, l, r);
if(l > m) return find(i + i + 1, m + 1, y, l, r);
ll p = find(i + i, x, m, l, m), q = find(i + i + 1, m + 1, y, m + 1, r);
return min(p, q);
}int main()
for(ll l = 1, r = ml; l <= r; )
f[i] ++;
if(f[i] > ml) l[++ ml] = a[i] - i;
l[f[i]] = min(l[f[i]], a[i] - i);
}fd(i, n, 1) next[++ tot] = final[f[i]], to[tot] = i, final[f[i]] = tot;
fo(l, 1, ml)
}ans = 1e18;
fo(i, 1, n) if(f[i] == ml)
ans = min(ans, s[i] + (n - i + 1) * a[i] + (n - i) * (n - i + 1) / 2);
printf("%lld %lld", ml, ans);
}
2011集訓隊出題 happiness
高一一班的座位表是個n m的矩陣,經過乙個學期的相處,每個同學和前後左右相鄰的同學互相成為了好朋友。這學期要分文理科了,每個同學對於選擇文科與理科有著自己的喜悅值,而一對好朋友如果能同時選文科或者理科,那麼他們又將收穫一些喜悅值。作為計算機競賽教練的scp大老闆,想知道如何分配可以使得全班的喜悅值總...
2011集訓隊出題 happiness
description 高一一班的座位表是個n m的矩陣,經過乙個學期的相處,每個同學和前後左右相鄰的同學互相成為了好朋友。這學期要分文理科了,每個同學對於選擇文科與理科有著自己的喜悅值,而一對好朋友如果能同時選文科或者理科,那麼他們又將收穫一些喜悅值。作為計算機競賽教練的scp大老闆,想知道如何分...
2011集訓隊出題 跳跳棋
time limits 1000 ms memory limits 128000 kb description 跳跳棋是在一條數軸上進行的。棋子只能擺在整點上。每個點不能擺超過乙個棋子。我們用跳跳棋來做乙個簡單的遊戲 棋盤上有3顆棋子,分別在a,b,c這三個位置。我們要通過最少的跳動把他們的位置移動...