Codeforces Round #747 (Div. 2) E2 —— 树形DP + 状态机

E2

题目
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述


思路

这题很像皇宫看守这道题;

因为我做过皇宫看守这道题,很自然就想到树形DP


我们定义 f ( i , j ) f(i,j) f(i,j)表示以 i i i为根的子树,状态为 j j j所拥有的方案数;

j = 0 j=0 j=0表示当前节点的颜色为任意
j = 1 j=1 j=1表示当前节点的颜色为黄、白;
j = 2 j=2 j=2表示当前节点的颜色为绿、蓝;
j = 3 j=3 j=3表示当前节点的颜色为红、橙;

也就是互斥的颜色放在同一组中;

我们设 j j j i i i的子节点;

f ( i , 0 ) ∗ = f ( j , 1 ) + f ( j , 2 ) f(i,0)*=f(j,1)+f(j,2) f(i,0)=f(j,1)+f(j,2)
f ( i , 1 ) ∗ = f ( j , 2 ) + f ( j , 3 ) f(i,1)*=f(j,2)+f(j,3) f(i,1)=f(j,2)+f(j,3)
f ( i , 2 ) ∗ = f ( j , 1 ) + f ( j , 3 ) f(i,2)*=f(j,1)+f(j,3) f(i,2)=f(j,1)+f(j,3)

因为这道题的结点数最大有 2 60 − 1 2^{60}-1 2601这么多,我们不可能全部存下来;

并且这棵树是一颗完美的二叉树,假设父节点为 p p p,那么子节点必然为 2 p 2p 2p 2 p + 1 2p+1 2p+1;

因此第一维的 i i i我们就可以优化掉,只需要一维用来表示颜色;

因为点数特别多,我们不可能全部扫一遍;

做过Easy版本就容易想到,后面的子节点如果是任意颜色的;

那么乘一个 4 4 4即可;

因此我们只需要维护部分点,其他点直接算出来;

Code

#include <iostream>
#include <cstdio>
#include <vector>
#include <map>
#include <algorithm>

using namespace std;

typedef long long ll;

int dep,k;

const int MOD = 1e9+7;

//点i的颜色是什么
//0表示任意颜色,1表示黄白,2表示绿蓝,3表示红橙
map<ll,int> mp;

vector<ll> dfs(ll u){
    vector<ll> dp = {0,2,2,2};
    if(mp[u] != 0){
        for(int i=1;i<=3;++i) dp[i] = (mp[u] == i);
    }
    vector<ll> sons = {u<<1,u<<1|1};
    for(auto to : sons){
        //退出递归
        if(mp.count(to)){
            auto dp_son = dfs(to);
            dp[1] = (dp[1]%MOD*(dp_son[2]+dp_son[3])%MOD)%MOD;
            dp[2] = (dp[2]%MOD*(dp_son[1]+dp_son[3])%MOD)%MOD;
            dp[3] = (dp[3]%MOD*(dp_son[1]+dp_son[2])%MOD)%MOD;  
        }
    }
    return dp;
}
int qpow(ll x,ll y){
    ll base = x,ret = 1;
    while(y){
        if(y&1){
            ret *= base;
            ret %= MOD;
        }
        base *= base;
        base %= MOD;
        y>>=1;
    }
    return ret%MOD;
}
void solve(){
    cin >> dep >> k;
    ll id,c;
    string color;
    while(k--){
        cin >> id >> color;
        auto &x = color[0];
        if(x == 'w' || x == 'y') c = 1;
        else if(x == 'g' || x == 'b') c = 2;
        else c = 3;
        mp[id] = c;
        //染色,只访问部分节点
        while(id > 1){
            id >>= 1;
            if(!mp.count(id)) mp[id] = 0;
        }
    }
    auto ans = dfs(1);
    ll sum = 0;
    for(auto x : ans) sum += x;
    ll tot = (1ll<<dep) - 1;
    tot -= mp.size();
    sum = (sum%MOD*qpow(4,tot)%MOD)%MOD;
    cout << sum << '\n';
}

int main(){
    std::ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
    solve();
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值