bzoj 5123: [Lydsy1712月赛]线段树的匹配

 

    设f[0/1][x]为区间[1,x]的根向下 不选(0)或者选(1)  的dp pair<最优值,方案数>。

    可以很容易的发现总状态数就是log级别的,因为2*n 与 (2*n+1 或者 2*n-1) 向下有很多重叠,记忆化搜索即可。

   

    初始化的话 f[0][1] = {0,1}, f[1][1] = {0,0} ,切记后者的方案数不能为1,不仅与事实不符,也会与前者重叠。

 

#include<bits/stdc++.h>
#include<tr1/unordered_map>
using namespace std;
using namespace std::tr1;
#define ll long long
const int ha=998244353;
inline void ADD(int &x,int y){ x+=y; if(x>=ha) x-=ha;}
struct node{
	ll M; int S;
	node operator +(const node &u)const{
		node r=u;
		if(M>r.M) r=*this;
		else if(M==r.M) ADD(r.S,S);
		return r;
	}
	node operator *(const node &u)const{
		return (node){M+u.M,S*(ll)u.S%ha};
	}
}A,B;
unordered_map<ll,node> f[2];

void dp(ll x){
	if(f[0].count(x)) return;
	ll mid=x>>1;
	dp(mid),dp(x-mid);
	
	f[0][x]=(f[0][mid]+f[1][mid])*(f[0][x-mid]+f[1][x-mid]);
	f[1][x]=f[0][mid]*f[1][x-mid]*A+f[1][mid]*f[0][x-mid]*A+f[0][mid]*f[0][x-mid]*B;
}

int main(){
	f[0][1]=(node){0,1},f[1][1]=(node){0,0};
	A=(node){1,1},B=(node){1,2};
	ll n; scanf("%lld",&n),dp(n);
	node ans=f[0][n]+f[1][n];
	printf("%lld %d\n",ans.M,ans.S);
	return 0;
}

  

 

posted @ 2018-04-25 08:46  蒟蒻JHY  阅读(273)  评论(0编辑  收藏  举报