-
-
-
-
- 傳送門
- 思路
- 正解
- 參考代碼
-
-
-
傳送門
思路
唉,我太弱了,什麼都不會,題又做不來,而且又讀錯題了。題目要求是切 k k 刀,我搞成了分成 kk 塊……幹脆
k++
算了。
這道題顯然可以 DP,但是我一開始的狀态是這樣的:
fi,j=max{fs,j−2+g(s+1,i)} f i , j = max { f s , j − 2 + g ( s + 1 , i ) }
其中 g(l,r) g ( l , r ) 表示把 l l 到 rr 分成兩段的最大價值,這個可以在均攤 O(1) O ( 1 ) 的時間複雜度内計算出。
然後我打開了一篇萬惡的讨論,看到了“斜率優化”四個字,然後我就嘗試在這個的基礎上變形,但是發現 g g 的計算方法除了均攤 O(1)O(1)(斜率優化時還不能用)就隻有最壞 O(logn) O ( log n ) 的求法了。沒辦法,又涼了……
正解
首先要發現這個狀态( ai a i 表示 1∼i 1 ∼ i 的和,即字首和):
fi,j=max{fs,j−1+as×(ai−as)} f i , j = max { f s , j − 1 + a s × ( a i − a s ) }
他們怎麼這麼有本事,雖然很顯然切的順序不影響結果,但是他們就想到了 先切後面這一招。
然後很顯然這個可以用斜率優化來做。雖然我學的斜率優化是假的,但是這并不重要,讓我們來試着用我的方法推一下。
我們省略 max max 函數,展開裡面的式子得:
fi,j=fs,j−1+as×ai−as×as f i , j = f s , j − 1 + a s × a i − a s × a s
其中 s s 表示決策。
若決策 ss 比決策 t t 更優( s>t s > t ),則有:
fs,j−1+as×ai−as×as>ft,j−1+at×ai−at×at f s , j − 1 + a s × a i − a s × a s > f t , j − 1 + a t × a i − a t × a t
将包含 i i 的移向一邊,不包含 ii 的移向另一邊,則有:
(fs−ft)−(a2s−a2t)>ai(at−as) ( f s − f t ) − ( a s 2 − a t 2 ) > a i ( a t − a s )
我們不妨設 at−as≠0 a t − a s ≠ 0 ,那麼顯然 at−as<0 a t − a s < 0 。則有:
(fs−ft)−(a2s−a2t)as−at>−ai ( f s − f t ) − ( a s 2 − a t 2 ) a s − a t > − a i
顯然不等式右邊非嚴格單調遞減。設 h(s,t) h ( s , t ) 等于左式( s>t s > t ),若 h(s,t)>−ai h ( s , t ) > − a i ,那麼 t t 永遠不再會是最優決策點。我們再設 s>t>ls>t>l,若 h(s,t)>h(t,l) h ( s , t ) > h ( t , l ) ,那麼 t t 永遠不會是最優決策點:若 h(s,t)>−aih(s,t)>−ai,則 t t 不是最優;若 h(s,t)≤−aih(s,t)≤−ai,則 h(t,l)<−ai h ( t , l ) < − a i , t t 依然不是最優。
現在唯一需要考慮的是 aj=aiaj=ai 的問題,這裡有一個一般人我都不告訴的技巧:将 as−at a s − a t 視為無限逼近 0 0 ,則斜率為正無窮,設成一個極大的數即可。
輸出方案那還不簡單,儲存從哪裡轉移過來的即可。
唉,我太弱了,就這麼個簡單的斜率優化我調了三節課,而且還沒有秒切,感覺自己學的斜率優化果然是假的。分母為 00 的情況也不是第一次見了,但是就是見一次忘一次,唉,太弱啦!
參考代碼
注意一定要讓
k++
,把 k k <script type="math/tex" id="MathJax-Element-47">k</script> 了解成段數而不是分割點數目,這樣的轉移才是對的。否則一開始的狀态(
dq.push()
)是錯的。
#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <cstring>
#include <cassert>
#include <cctype>
#include <climits>
#include <ctime>
#include <iostream>
#include <algorithm>
#include <vector>
#include <string>
#include <stack>
#include <queue>
#include <deque>
#include <map>
#include <set>
#include <bitset>
#include <list>
#include <functional>
#define loop register int
typedef long long LL;
typedef unsigned long long ULL;
using std::cin;
using std::cout;
using std::endl;
typedef LL INT_PUT;
INT_PUT readIn()
{
INT_PUT a = ; bool positive = true;
char ch = getchar();
while (!(ch == '-' || std::isdigit(ch))) ch = getchar();
if (ch == '-') { positive = false; ch = getchar(); }
while (std::isdigit(ch)) { a = a * - (ch - '0'); ch = getchar(); }
return positive ? -a : a;
}
void printOut(INT_PUT x)
{
char buffer[]; int length = ;
if (x < ) putchar('-'); else x = -x;
do buffer[length++] = -(x % ) + '0'; while (x /= );
do putchar(buffer[--length]); while (length);
}
const int maxn = int() + ;
int n, k;
int a[maxn];
LL f[][maxn];
struct Deque
{
int c[maxn];
int head, tail;
void clear() { head = tail = ; }
int size() { return tail - head; }
void push(int x) { c[tail++] = x; }
void pop_front() { head++; }
void pop_back() { tail--; }
int front() { return c[head]; }
int front2() { return c[head + ]; }
int back() { return c[tail - ]; }
int back2() { return c[tail - ]; }
} dq;
inline LL calcY(int i, int s) { return f[i][s] - (LL)a[s] * a[s]; }
inline LL deltaY(int i, int s, int t) { return calcY(i, s) - calcY(i, t); }
inline double slope(int i, int s, int t) { if (a[s] == a[t]) return ; return (double)deltaY(i, s, t) / (a[s] - a[t]); }
inline LL calc(int i, int j, int s)
{
return f[i][s] + (LL)a[s] * (a[j] - a[s]);
}
int pre[][maxn];
void run()
{
n = readIn();
k = readIn();
for (loop i = ; i <= n; i++)
a[i] = readIn();
for (loop i = ; i <= n; i++)
a[i] += a[i - ];
k++;
for (loop i = ; i <= k; i++) // note
{
dq.clear();
dq.push(i - ); // note
for (loop j = i; j <= n; j++)
{
while (dq.size() > && slope(i - , dq.front2(), dq.front()) > -a[j])
dq.pop_front();
f[i][j] = calc(i - , j, dq.front());
pre[i][j] = dq.front();
while (dq.size() > && slope(i - , j, dq.back()) > slope(i - , dq.back(), dq.back2()))
dq.pop_back();
dq.push(j);
}
}
printOut(f[k][n]);
putchar('\n');
std::vector<int> ans;
int cnt = n;
int t = k;
for (int i = k; i; cnt = pre[i][cnt], i--)
ans.push_back(pre[i][cnt]);
for (int i = ans.size() - ; ~i; i--)
{
printOut(ans[i]);
putchar(' ');
}
}
int main()
{
run();
return ;
}