1. 程式人生 > >【bzoj5123】[Lydsy12月賽]線段樹的匹配 樹形dp+記憶化搜索

【bzoj5123】[Lydsy12月賽]線段樹的匹配 樹形dp+記憶化搜索

記憶 兩種 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+記憶化搜索