bzoj5123: 线段树的匹配
http://www.lydsy.com/JudgeOnline/upload/201712/prob12.pdf
dp[len][0/1] 表示节点表示区间长度为len,节点选/不选的 最大匹配
sum[len][0/1] 表示对应dp[len][0/1]的方案数
这里选节点即选节点与其父节点的边
设区间长度为len,左子区间长度为L,右子区间长度为R
这个节点选,那么左右子节点都不能选
dp[len][1]=1+dp[L][0]+dp[R][0]
sum[len][1]=sum[L][0]*sum[R][0]
这个节点不选,有3种情况:
左右子节点都不选:
dp[len][0]=dp[L][0]+dp[R][0]
sum[len][0]=sum[L][0]+sum[R][0]
选左子节点:
dp[len][0]=dp[L][1]+dp[R][0]
sum[len][0]=sum[L][1]+sum[R][0]
选右子节点:
dp[len][0]=dp[L][0]+dp[R][1]
sum[len][0]=sum[L][0]+sum[R][1]
如果dp[len][0] 在三种情况中有相同的,sum[len][0]要累加
len虽然是1e18,但只会用log种,所以用map
#include<map> #include<cstdio> #include<cstring> using namespace std; typedef long long LL; const int mod=998244353; //int dp[100001][2]; //int sum[10001][2]; map<LL,LL>dp[2]; map<LL,LL>sum[2]; void dfs(LL len) { if(len==1) { dp[0][1]=0; sum[0][1]=1; dp[1][1]=1; sum[1][1]=1; return; } LL R=len>>1; LL L=len-R; if(dp[0].find(L)==dp[0].end()) dfs(L); if(dp[0].find(R)==dp[0].end()) dfs(R); dp[1][len]=1+dp[0][L]+dp[0][R]; sum[1][len]=sum[0][L]*sum[0][R]%mod; dp[0][len]=dp[1][len]-1; sum[0][len]=sum[1][len]; if(dp[0][L]+dp[1][R]>dp[0][len]) { dp[0][len]=dp[0][L]+dp[1][R]; sum[0][len]=sum[0][L]*sum[1][R]%mod; } else if(dp[0][L]+dp[1][R]==dp[0][len]) { sum[0][len]=(sum[0][len]+sum[0][L]*sum[1][R])%mod; } if(dp[1][L]+dp[0][R]>dp[0][len]) { dp[0][len]=dp[1][L]+dp[0][R]; sum[0][len]=sum[1][L]*sum[0][R]%mod; } else if(dp[1][L]+dp[0][R]==dp[0][len]) { sum[0][len]=(sum[0][len]+sum[1][L]*sum[0][R])%mod; } } int main() { LL n; scanf("%I64d",&n); dfs(n); printf("%I64d %I64d",dp[0][n],sum[0][n]); }