【bzoj5123】[Lydsy12月賽]線段樹的匹配 樹形dp+記憶化搜索
阿新 • • 發佈:2018-01-03
記憶 兩種 spa post mes efi http 搜索 style
題目描述
求一棵 $[1,n]$ 的線段樹的最大匹配數目與方案數。
$n\le 10^{18}$
題解
樹形dp+記憶化搜索
設 $f[l][r]$ 表示根節點為 $[l,r]$ 的線段樹,匹配選擇根節點的最大匹配&方案數,$g[l][r]$ 表示根節點為 $[l,r]$ 的線段樹,匹配不選擇根節點的最大匹配&方案數。那麽這是一個很普通的樹形dp。
註意到區間長度相等的線段樹的結果是一樣的,且每層至多有兩種區間長度不同的區間(參考 這題 ),因此直接以區間長度為狀態進行記憶化搜索即可。
這裏偷懶使用了map,時間復雜度 $O(\log^2 n)$
#include <map> #include <cstdio> #define mod 998244353 using namespace std; typedef long long ll; struct data { ll x , y; data() {} data(ll a , ll b) {x = a , y = b;} data operator+(const data &a)const {return data(x + a.x , y * a.y % mod);} data operator*(const data &a)const { if(x > a.x) return *this; if(x < a.x) return a; return data(x , (y + a.y) % mod); } }; struct node { data f , g; node() {} node(data a , data b) {f = a , g = b;} }; map<ll , node> mp; node dfs(ll n) { if(mp.find(n) != mp.end()) return mp[n]; node l = dfs(n - (n >> 1)) , r = dfs(n >> 1); return mp[n] = node((l.f + r.g + data(1 , 1)) * (l.g + r.f + data(1 , 1)) * (l.g + r.g + data(1 , 2)) , (l.f + r.f) * (l.f + r.g) * (l.g + r.f) * (l.g + r.g)); } int main() { mp[1] = node(data(-1 , 0) , data(0 , 1)); ll n; scanf("%lld" , &n); node tmp = dfs(n); data ans = tmp.f * tmp.g; printf("%lld %lld\n" , ans.x , ans.y); return 0; }
【bzoj5123】[Lydsy12月賽]線段樹的匹配 樹形dp+記憶化搜索