P5241 序列(滾動陣列 字首和優化dp)

2022-04-30 08:57:08 字數 1884 閱讀 6932

p5241 序列

挺神仙的一題

看看除了dp好像沒什麼其他辦法了

想著怎麼構個具體的圖出來,然鵝不太現實。

於是我們想辦法用幾個引數來表示dp陣列

加了幾條邊肯定要的吧,於是加個引數$i$表示已加了$i$條邊

這顯然是不夠的。於是我們又想:強連通分量.....連通塊.......

於是加個$j$表示還有$j$個強連通分量

於是dp陣列為$f[i][j]$

這是我們發現乙個問題,狀態$f[i][j]$不一定是合法的。

那dp不就gg了嗎

再次撕烤,我們發現每次加上的邊無非就3種情況:

1.把2個強連通分量(或鏈)連成一條鏈

2.在某個強連通分量中瞎連(沒啥用)

3.在1條鏈上的某點向回連,形成乙個環,縮成乙個新強連通分量(可以減少任意個強連通分量

我們設$k-1$條邊(dp陣列下標$k$為正數較好處理)投入到第3種情況

要生成剩下$j$個強連通的情況,我們最少投入$n-j$條邊用於第1種情況

所以$n-j+(k-1)<=i$

我們又發現,要生成剩下$j$個強連通的情況,我們最多共投入的邊數$i$是有限制的

最多情況就是1個塊有$n-j+1$個點,剩下$j-1$個塊只有1個點,藍後大塊每個點連$n-1$條邊,小塊互相之間弱連通

那麼最大邊數為$(n-j+1)*(n-1)+(j-2+j-3+j-4+...+1)=(n-j+1)*(n-1)+(j-1)*(j-2)/2$

所以$i<=(n-j+1)*(n-1)+(j-1)*(j-2)/2$

總結一下,即設$f[i][j][k]$表示到第$i$條邊,有$j$個強連通分量,$k-1$條邊向回連的方案數

限制條件:

$n-j+(k-1)<=i$

$i<=(n-j+1)*(n-1)+(j-1)*(j-2)/2$

轉移:$f[i][j][k]+=f[i-1][j][k]$(第2種情況)

$f[i][j][k]+=\sum_^f[i-1][h][k-1]$

顯然是可以滾動陣列+字首和優化的辣

然鵝複雜度還是太高,主要因為k很麻煩

仔細觀察k,發現

$n-j+(k-1)<=i$

$k<=i+j-n+1$

發現$i>=2n$時k總是合法的

於是我們就可以愉快地縮成2維辣

#include#include

#include

#define rint register int

using

namespace

std;

inline

int min(int a,int b)

const

int mod=1e9+7

;inline

int md(int x)

#define n 405

int n,f[2][n][n],sf[2][n][n],g[2][n],sg[n][n],lim[n],ans[n*n];

intmain()

}w=1

;

for(rint j=1;j<=n;++j)

for(rint k=1;k<=n;++k)

g[0][j]=md(g[0][j]+f[0

][j][k]);

for(rint j=n;j;--j) sg[0][j]=md(sg[0][j+1]+g[0

][j]);//降維

for(rint i=tn+1;i<=n*(n-1);++i,w^=1

) }

for(rint i=1;i<=n*(n-1);++i) printf("

%d "

,ans[i]);

return0;

}

P1631 序列合併

做法 將 a 和 b 都從小到大排一遍序。然後組成這樣乙個矩陣 a1 b1 a1 b2 a1 b3 a1 bn a2 b1 a2 b2 a2 b3 a2 bn a3 b1 an b1 an b2 an b3 an bn 正確性 我們先把每行的頭扔進堆裡,每行的頭是每行中的最小值,所以這樣我們能在堆裡...

P1631 序列合併

有兩個長度都是n的序列a和b,在a和b中各取乙個數相加可以得到n 2個和,求這n 2個和中最小的n個。輸入格式 第一行乙個正整數n 第二行n個整數ai,滿足ai ai 1且ai 10 9 第三行n個整數bi,滿足bi bi 1且bi 10 9.資料規模 對於50 的資料中,滿足1 n 1000 對於...

P1631 序列合併

有兩個長度都是n的序列a和b,在a和b中各取乙個數相加可以得到 n 2n2 個和,求這 n 2n2 個和中最小的n個。輸入格式 第一行乙個正整數n 第二行n個整數 a iai 滿足 a i le a ai ai 1 且 a i le 10 9ai 10 9 第三行n個整數 b ibi 滿足 b i ...