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; }
我爱学习,学习使我快乐