題目鏈結
首先要發現乙個重要的性質:分割的順序是不會影響答案的
證明:這樣我們就可以愉快地dp啦首先對於沒有交的兩段區間,顯然先後順序改變不會有影響
而對於在同一段區間上的兩次分割:
設有一段序列由長度為\(x,y,z\)的三段拼接起來
如果先分割\(xy\)和\(z\),再分割\(x\)和\(y\),答案是\((x+y)*z+x*y\)
而如果先分割\(x\)和\(yz\),再分割\(y\)和\(z\),答案是\(x*(y+z)+y*z\)
可以發現,兩個答案都是\(xy+xz+yz\),即分割順序不影響答案
設\(f[i][j]\)為將\(1\sim i\)這段分割\(j\)次產生的代價,\(s[i]\)為\(1\sim i\)段的總長,則有:
\[f[i][j]=f[k][j-1]+s[k]*(s[i]-s[k]) \quad k\in [1,i-1]
\]分割次數這一維可以滾動優化空間,設上一次的為\(g[i]\),這一次要求的是\(f[i]\)
上面的式子就是
\[f[i]=g[k]+s[k]*(s[i]-s[k]) \quad k\in [1,i-1]
\]哎,感覺很像可以斜率優化的式子啊,那麼...
如果從\(k\)轉移比從\(j\)轉移更優,則:
\[g[k]+s[k]*(s[i]-s[k]) > g[j]+s[j]*(s[i]-s[j])
\]再化一化
\[\frac >s[i]
\]於是我們就可以用斜率優化dp啦,
注意這題精度卡的簡直喪心病狂...算斜率的時候一定要先算減再乘\(1.0\)
#include#include#include#include#include#include#includeusing namespace std;
typedef long long ll;
inline ll read()
while(ch>='0'&&ch<='9')
ans=(ans<<1)+(ans<<3)+ch-'0',ch=getchar();
return ans*fh;
}const int maxn=1e5+100;
const long double inf=1e18;
int n,k,st[maxn],tp,pre[210][maxn],q[maxn];
ll s[maxn],f[maxn],g[maxn];
inline long double slope(int x,int y)
int main()
memcpy(g,f,sizeof(g));
} printf("%lld\n",g[n]);
for(int i=pre[k][n];i;i=pre[--k][i]) st[++tp]=i;
while(tp) printf("%d ",st[tp--]);
return 0;
}
P3648 APIO2014 序列分割
part1 首先看到題目,嗯 o o很騷 手玩一波樣例之後發現狀態很好想 這裡簡單地任務階段可以被劃分次數 也就是劃分順序 和劃分位置來劃分 初步想法是 f i j 表示前 i 次最後一次切的是 j 位置 隨後意識到沒法通過上一層進行轉移,這裡出現問題也是正常,因為沒有進行更深入地發掘性質 此處無法...
P3648 APIO2014 序列分割
傳送門 首先容易證明,得分和切的順序沒有關係 所以直接預設先切左邊再切右邊就好了 然後顯然可以 dp 一開始想的是設 f i j 表示切了 i 次,此次把 j 和 j 1 分開,得到的最大價值 那麼顯然列舉上一次切的位置 k 那麼 f i j f i 1 k sum j sum k sum n su...
洛谷 3648 APIO2014 序列分割
題目描述 你正在玩乙個關於長度為 n 的非負整數序列的遊戲。這個遊戲中你需要把序列分成 k 1 個非空的塊。為了得到 k 1 塊,你需要重複下面的操作 k 次 選擇乙個有超過乙個元素的塊 初始時你只有一塊,即整個序列 選擇兩個相鄰元素把這個塊從中間分開,得到兩個非空的塊。每次操作後你將獲得那兩個新產...