51Nod 1780 完美序列

2021-08-04 02:37:12 字數 2050 閱讀 7318

acm模版

首先,我們先來分析一下如何構造才合法。

先預處理出來每種大小的數的個數,並在這個過程進行判斷是否連續(不大於

1 ),然後,我們可以從小到大進行插空法插數,那麼如何插呢?假如,此時我們已經查到數

i,那麼合法的插孔分為兩種,第一種是插在兩個 i−

1 之間,另一種就是當首尾有 i−

1 時,我們可以在首尾兩側進行插空。那麼我們需要考慮的也就是此時考慮到了第

i 種數、有多少個相鄰的第

i種數對兒、首尾有幾個第

i 種數,自然最後這個狀態只有三種,就是 0、

1、2,所以我們設 dp

[i][

j][0

/1/2

] 表示此時考慮到了第

i 種數時,首尾第

i種數的個數分別為 0、

1、2 ,相鄰的第

i 種數對兒個數加上首尾第

i種數的個數的和為

j 時的方案數。初始化,我們應該設 dp

[1][

cnt[

1]+1

][2]

=1,至於為什麼,很容易理解,此時,第一種數的方案只有一種,首尾第一種數的個數為

2 ,連續的第一種數對兒為 cn

t[1]

−1,所以,你沒有看錯,cn

t[1]

+1實際上就是此時插空時允許插空的數目。

綜上所述,這個題核心就是插空法,組合數學的知識,另外還要使用一次插空法的地方是,要考慮將 cn

t[i]

個數劃分為

k 份時。

哦,對了,最後結果自然是不同插空數時三種情況的和的和嘍!也就是說需要控制該 dp

的第二維的量,因為我們說明了,第二維實際上就是此時允許的插空數目。over~~~

#include 

#include

#include

#include

using

namespace

std;

typedef

long

long ll;

const

int maxn = 3e4 + 5;

const

int maxm = 111;

const

int mod = 1e9 + 7;

int n;

int a[maxn];

int cnt[maxn];

ll c[maxm][maxm];

ll dp[maxn][maxm][3];

template

inline

bool scan_d(t &ret)

while (c != '-' && (c < '0' || c > '9'))

sgn = (c == '-') ? -1 : 1;

ret = (c == '-') ? 0 : (c - '0');

while (c = getchar(), c >= '0' && c <= '9')

ret *= sgn;

return1;}

void init()

}}int main()

if (a[i] - a[i - 1] > 1)

if (a[i] - a[i - 1] == 1)

else

}dp[1][cnt[1] + 1][2] = 1;

for (int i = 1; i < tot; i++)

if (x - k + 1 >= 0)

if (k - 2 >= 0 && x - k + 2 >= 0)}}

}ll ans = 0;

for (int i = 0; i <= cnt[tot] + 1; i++)

cout

<< ans << endl;

return

0;}

51Nod1623 完美消除

link solution 首先我們可以發現乙個結論,對於乙個數 x 它的最低修改次數為它每位與前去中是否都比此位上的數大,有則答案 1 因為若有小數則沒有辦法將其答案貢獻變低。這個東西可以直接單調棧維護乙個遞增序列。所以這樣 dp 狀態也很顯然了,設 f 表示當前到 i 位,答案為 j sta 表...

51Nod1367 完美森林 貪心

有一棵n個點的樹,樹中節點標號依次為0,1,2,n 1,其中n 500000。樹中有n 1條邊,這些邊長度不一定相同。現在要把樹中一些邊刪除,設刪除了k條邊 k 0,即可以不刪除任何邊 由樹的性質可知,該樹將被分割為乙個含有k 1棵樹的森林。稱乙個森林是 完美森林 要求這個森林中的每一棵樹滿足 該樹...

51nod 序列分解(dfs)

1400 序列分解 基準時間限制 1 秒 空間限制 131072 kb 分值 40 難度 4級演算法題 小刀和大刀是雙胞胎兄弟。今天他們玩乙個有意思的遊戲。大刀給小刀準備了乙個長度為n的整數序列。小刀試著把這個序列分解成兩個長度為n 2的子串行。這兩個子串行必須滿足以下兩個條件 1.他們不能相互重疊...