【bzoj5123】[Lydsy12月赛]线段树的匹配 树形dp+记忆化搜索

题目描述

求一棵 $[1,n]$ 的线段树的最大匹配数目与方案数。

$n\le 10^{18}$


题解

树形dp+记忆化搜索

设 $f[l][r]$ 表示根节点为 $[l,r]$ 的线段树,匹配选择根节点的最大匹配&方案数,$g[l][r]$ 表示根节点为 $[l,r]$ 的线段树,匹配不选择根节点的最大匹配&方案数。那么这是一个很普通的树形dp。

注意到区间长度相等的线段树的结果是一样的,且每层至多有两种区间长度不同的区间(参考 这题 ),因此直接以区间长度为状态进行记忆化搜索即可。

这里偷懒使用了map,时间复杂度 $O(\log^2 n)$

#include 
#include 
#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 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;
}

 

转载于:https://www.cnblogs.com/GXZlegend/p/8182253.html

你可能感兴趣的:(【bzoj5123】[Lydsy12月赛]线段树的匹配 树形dp+记忆化搜索)