Codeforces1594 E2. Rubik‘s Cube Coloring (hard version)(思维+dp记忆化搜索)

题意:

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

解法:
先预处理d[c][k]表示根节点颜色为c,深度为k的满二叉树方案数.
0<=c<6,1<=k<=60,数据范围较小,因此可以很快的预处理.

如果一颗子树,所有点都没有颜色,那么可以用预处理的数组直接O(1)给出方案数.
因此我们只需要关注有带色点的子树.
由于只有2000个点有颜色,k<=60,因此最多2000*60个点是我们需要关注的,
我们对这2000*60个点单独记忆化搜索即可:
令d2[id][c]表示id点颜色为c,子树的方案数,
其中id是点的编号,c是点的颜色.

d[][]和d2[][]的转移:
枚举左右子节点的颜色计算方案数即可.

算法复杂度显然为O(能过).
code:
#include<bits/stdc++.h>
// #define MULTI_CASE
#define SYNC_OFF
#define PI pair<int,int>
#define int long long
using namespace std;
// const int mod=998244353;
const int mod=1e9+7;
const int maxm=2e6+5;
map<int,int>col;
map<int,int>idx;int num;
map<int,bool>mark;//标记子树中至少有一个颜色的点
int lim[10][10];
int d[10][66];
int d2[2222*66][6];
int n,k;
map<char,int>c_mp={
    {'w',0},
    {'y',1},
    {'g',2},
    {'b',3},
    {'r',4},
    {'o',5},
};
/*
0是白 白黄
1是黄 白黄
2是绿 绿蓝
3是蓝 绿蓝
4是红 红橙
5是橙 红橙
*/
void init(){
    //初始化lim[][]
    lim[0][0]=lim[0][1]=1;
    lim[1][0]=lim[1][1]=1;
    lim[2][2]=lim[2][3]=1;
    lim[3][2]=lim[3][3]=1;
    lim[4][4]=lim[4][5]=1;
    lim[5][4]=lim[5][5]=1;
}
int dp_dfs(int c,int k){
    if(k==1)return 1;
    if(d[c][k]!=-1)return d[c][k];
    d[c][k]=0;
    for(int i=0;i<6;i++){//左边的颜色
        if(lim[c][i])continue;
        for(int j=0;j<6;j++){//右边的颜色
            if(lim[c][j])continue;
            d[c][k]=(d[c][k]+dp_dfs(i,k-1)*dp_dfs(j,k-1)%mod)%mod;
        }
    }
    return d[c][k];
}
int dfs(int c,int k,int id){
    if(col.count(id)&&col[id]!=c)return 0;//判断合法性
    if(k==1)return 1;//最后一层
    //如果子树中所有点都没有颜色,那么直接返回预处理的结果
    if(!mark.count(id))return d[c][k];
    //如果至少有一个子节点有颜色,那么记忆化搜索
    if(!idx.count(id))idx[id]=++num;
    int x=idx[id];//获取映射后的下标
    if(d2[x][c]!=-1)return d2[x][c];
    d2[x][c]=0;
    int l=id*2,r=id*2+1;
    for(int i=0;i<6;i++){//左边的颜色
        if(lim[c][i])continue;
        for(int j=0;j<6;j++){//右边的颜色
            if(lim[c][j])continue;
            d2[x][c]=(d2[x][c]+dfs(i,k-1,l)*dfs(j,k-1,r)%mod)%mod;
        }
    }
    return d2[x][c];
}
void solve(){
    init();
    cin>>k>>n;
    //初始化d[][]
    memset(d,-1,sizeof d);
    memset(d2,-1,sizeof d2);
    for(int i=0;i<6;i++){
        dp_dfs(i,k);
    }
    //
    for(int i=1;i<=n;i++){
        int x;cin>>x;
        char s[10];cin>>s;
        col[x]=c_mp[s[0]];//点x的颜色
        while(x){//O(log)标记所有祖先节点
            mark[x]=1;
            x/=2;
        }
    }
    int ans=0;
    for(int i=0;i<6;i++){
        ans=(ans+dfs(i,k,1))%mod;
    }
    cout<<ans<<endl;
}
void Main(){
    #ifdef MULTI_CASE
    int T;cin>>T;while(T--)
    #endif
    solve();
}
void Init(){
    #ifdef SYNC_OFF
    ios::sync_with_stdio(0);cin.tie(0);
    #endif
    #ifndef ONLINE_JUDGE
    freopen("../in.txt","r",stdin);
    freopen("../out.txt","w",stdout);
    #endif
}
signed main(){
    Init();
    Main();
    return 0;
}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值