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

Posted GXZlegend

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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 <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+记忆化搜索的主要内容,如果未能解决你的问题,请参考以下文章

BZOJ4919[Lydsy六月月赛]大根堆 线段树合并

bzoj 4881 [Lydsy1705月赛]线段游戏

BZOJ4919[Lydsy1706月赛]大根堆-------------线段树进阶

BZOJ4950lydsy七月月赛 C 二分图最大匹配

BZOJ5073[Lydsy十月月赛]小A的咒语 DP

BZOJ4919[Lydsy六月月赛]大根堆