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]);
}
原文地址:https://www.cnblogs.com/TheRoadToTheGold/p/8148895.html