簡單的計數題
首先題意可以轉化為:給你乙個長度為 n
nn 的序列 c
cc,求將 c
cc 分成兩個長度為 n
2\dfrac
2n 的相同的子串行的方案數。
考慮 dp,設 f(i
,sta
)f(i,sta)
f(i,st
a)表示已經將 c
cc 的前 i
ii 位分成了兩個子串行,其中長的子串行比短的子串行多出來的未匹配的部分為 sta
stast
a(用deque
、list
等stl容器均可記錄)的方案數。不妨稱這個 sta
stast
a 為狀態,那麼只有當狀態長度不大於 n
2\dfrac
2n 時,這個狀態才是有用的。
轉移十分簡單:新加入乙個元素時,考慮是繼續扔到當前狀態的尾部,還是和當前狀態的開頭匹配並刪除這個開頭。
這裡主要講為什麼時間是對的:
時間複雜度和狀態數有關。假設當前到第 i
ii 位,一共有 x
xx 個狀態,考慮接下來的 c
cc 的兩個元素 a,b
a,ba,
b:於是此時總狀態不超過 3n2
3^}32
n種。但還是太多了。
由於我們只需要知道 f(n
,∅
)f(n,\empty)
f(n,∅)
,所以考慮折半:將 c
cc 序列的前一半和後一半(要翻轉)分別 dp,最後將前後兩半的 dp 值合併起來統計答案。
此時狀態數不超過 3n4
3^}34
n種。然後題解說進一步分析(?)可以證明在 n=60
n=60
n=60
時狀態數不超過 50000
50000
50000。
**如下:
#include
#define n 65
using
namespace std;
namespace modular
inline
intdec
(int x,
int y)
inline
intmul
(int x,
int y)
}using
namespace modular;
inline
intread()
while
(ch>=
'0'&&ch<=
'9')
return x*f;
}typedef deque<
int> sta;
int t,n,hn,a[n];
sta now1,now2;
mapint>dp[2]
[n>>1]
;void
solve
(bool opt)
now2=now1;
now2.
push_back
(a[i+1]
);if(
!now1.
empty()
) dp[opt]
[i+1
][now2]
=add
(dp[opt]
[i+1
][now2]
,dp[opt]
[i][now1]);
else dp[opt]
[i+1
][now2]
=add
(dp[opt]
[i+1
][now2]
,mul
(dp[opt]
[i][now1],2
));}
}}intmain()
if(!now2.
empty()
) ans=
add(ans,
mul(
(*it)
.second,
mul(dp[1]
[hn]
[now2]
,inv2)))
;else ans=
add(ans,
mul(
(*it)
.second,dp[1]
[hn]
[now2]))
;}printf
("%d\n"
,ans);}
return0;
}/*52
1 12
2 24
1 1 2 2
61 2 3 4 5 6
41 2 2 1
*/
實測deque
比list
快,我直接疑惑了。 XSY3156 簡單計數II 容斥 DP
定義乙個序列的權值為 把所有相鄰的相同的數合併為乙個集合後,所有集合的大小的乘積。特別的,第乙個數和最後乙個數是相鄰的。現在你有 n 種數,第 i 種有 c i 個。求所有不同的序列的權值的和。n leq 50,c i leq 100 考慮第乙個數和最後乙個數不相鄰時怎麼做。記 g 為出現了 i 次...
666RPG 計數dp簡單題
lililalala正在玩一種有 n n個回合的回合制rpg遊戲,初始分數為0,第 i i個回合lililalala有如下兩種選擇。a.將分數加上 ai ai b.將分數 1 1 lililalala同樣也很討厭野獸數 666 666,但是他很卻喜歡數字 666 666。他想知道有多少種不同的方案使...
poj 幾道簡單的dp題
題意 求使數列程先遞增後遞減的形式需要去掉的數字個數。當然也可以直接遞減或者只遞減不遞增。分析 用最長遞增子串行的方法求,然後列舉兩個起點的位置即可。include include include using namespace std const int inf 1e8 const int n 1...