题意:给定一棵表示 [1, n] 的线段树,请求出它的最大匹配中有多少条边,并求出有多少种最大匹配的方案。
显然线段树形态由长度唯一确定,因此可以用(长度,根节点是否和子树的节点匹配)作为状态进行记忆化搜索
#include<bits/stdc++.h>
using namespace std;
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
For(j,m-1) cout<<a[i][j]<<' ';\
cout<<a[i][m]<<endl; \
}
#pragma
#define
typedef long long ll;
typedef long double ld;
typedef unsigned long long ull;
ll mul(ll a,ll b){return (a*b)%F;}
ll add(ll a,ll b){return (a+b)%F;}
ll sub(ll a,ll b){return ((a-b)%F+F)%F;}
inline int read()
{
int x=0,f=1; char ch=getchar();
while(!isdigit(ch)) {if (ch=='-') f=-1; ch=getchar();}
while(isdigit(ch)) { x=x*10+ch-'0'; ch=getchar();}
return x*f;
}
typedef pair<ll,ll> pll;
map<pair<ll,bool> ,pll > f;
void upd(pair<ll,ll> &a, pair<ll,ll> b) {
if (a.fi!=b.fi) a=max(a,b);
else a.se=(a.se+b.se)%F;
}
pll composed(pll a,pll b) {
return mp(a.fi+b.fi, (a.se*b.se)%F);
}
pll Max(pll a,pll b) {
if (a.fi!=b.fi) return max(a,b);
else return mp(a.fi,(a.se+b.se)%F);
}
pll dfs(ll x,bool b) {
if(f.find(mp(x,b))!=f.end()) return f[mp(x,b)];
pll l0=dfs(x>>1,0),l1=dfs(x>>1,1),r0=dfs((x-(x>>1)),0),r1=dfs((x-(x>>1)),1);
pll c00=composed(l0,r0),c01=composed(l0,r1),c10=composed(l1,r0),c11=composed(l1,r1);
if (!b) {
f[mp(x,0)] = Max(c00,Max(c01,Max(c10,c11)));
}else {
f[mp(x,1)] = Max(composed(c00 , mp(1,2)) ,composed(Max(c01,c10), mp(1,1)) );
}
return f[mp(x,b)];
}
int main()
{
// freopen("bzoj5123.in","r",stdin);
// freopen(".out","w",stdout);
f[mp(1,0)]=mp(0,1);
f[mp(1,1)]=mp(-1000,0);
ll n;
cin>>n;
pll ans=dfs(n,0),ans2=dfs(n,1);
upd(ans,ans2);
cout<<ans.fi<<' '<<ans.se<<endl;
return 0;
}