Luogu 3648 APIO2014 序列分割

2022-04-10 03:36:06 字數 1530 閱讀 2611

題目鏈結

首先要發現乙個重要的性質:分割的順序是不會影響答案的

證明:

首先對於沒有交的兩段區間,顯然先後順序改變不會有影響

而對於在同一段區間上的兩次分割:

設有一段序列由長度為\(x,y,z\)的三段拼接起來

如果先分割\(xy\)和\(z\),再分割\(x\)和\(y\),答案是\((x+y)*z+x*y\)

而如果先分割\(x\)和\(yz\),再分割\(y\)和\(z\),答案是\(x*(y+z)+y*z\)

可以發現,兩個答案都是\(xy+xz+yz\),即分割順序不影響答案

這樣我們就可以愉快地dp啦

設\(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 次 選擇乙個有超過乙個元素的塊 初始時你只有一塊,即整個序列 選擇兩個相鄰元素把這個塊從中間分開,得到兩個非空的塊。每次操作後你將獲得那兩個新產...