• CF1594 E2. Rubik's Cube Coloring (hard version)


    做出来了但是没交


    题意

    白色节点不能与白色和黄色节点相邻;
    黄色节点不能与白色和黄色节点相邻;
    绿色节点不能与绿色和蓝色节点相邻;
    蓝色节点不能与绿色和蓝色节点相邻;
    红色节点不能与红色和橙色节点相邻;
    橙色节点不能与红色和橙色节点相邻;

    据此给一个高为n((n<60))的完全二叉树染色。求方案数:

    不难推出公式: 设(fi)表示以高度为i的树,确定根节点颜色,有多少方案, (f_i=f_i^2*16)(根节点确定, 每个子节点有4种可行颜色)

    E2在E1的基础上, 有M个点的的颜色已经给出, 求给其他点染色的方案数。


    题解

    我们先把E1的(f_{i, j})数组预处理出来(高度为i的树没有预给定颜色, 且根节点颜色为j的方案数)

    考虑直接dp:如果这个节点的子树中没有预定义节点, 直接返回f数组, 否则直接暴力dp

    此时需要处理的节点个数为子树中包含预定义节点的节点个数, 可以证明这样的点数量为(O(nm))(每个预定义点都在叶子节点最大)

    现在的问题是如何判断一个点是否包含预定义节点呢:
    一种暴力的做法是直接枚举所有给出的点, 求出高度进行判断。但是这样的复杂度多了nm的常数
    考虑反过来, 对于每个预定义点, 把它的所有祖先的标记一下, 不难想到, 让节点编号每次除2就行。需要一个set维护, 同时还需要一个map来记录颜色。

    总复杂度(O(nmlog(nm)))以及一个较大的常数


    实现

    赛时代码过丑抱歉

    #include <iostream>
    #include <cstdio>
    #include <algorithm>
    #include <vector>
    #include <string>
    #include <cassert>
    #include <cstdlib>
    #include <map>
    #include <set>
    #define int long long
    using namespace std;
    
    int read(){
    	int num=0, flag=1; char c=getchar();
    	while(!isdigit(c) && c!='-') c=getchar();
    	if(c=='-') flag=-1, c=getchar();
    	while(isdigit(c)) num=1ll*num*10+c-'0', c=getchar();
    	return num*flag; 
    }
    
    int readc(){
    	char c=getchar();
    	while(c<'a' || c>'z') c=getchar();
    	return c;
    }
    
    void reads(){
    	char c=getchar();
    	while(c<'a' || c>'z') c=getchar();
    	while(c>='a' && c<='z') c=getchar();
    }
    
    int checkColor(int a, int b){
    	if(a > b) swap(a, b);
    	if(a == b) return 1;
    	if(a==4 && b==5) return 1;
    	if(a==2 && b==3) return 1;
    	if(a==0 && b==1) return 1;
    	return 0;
    }
    
    const int N = 3055, M = 5e6;
    const int mod = 1e9+7;
    int T, n, m;
    int r[M][6], sz=0;
    set<int> st;
    map<int, int> col;
    
    int get(char s){
    	if(s == 'w') return 0;
    	if(s == 'y') return 1;
    	if(s == 'g') return 2;
    	if(s == 'b') return 3;
    	if(s == 'r') return 4;
    	return 5; 
    }
    
    int p[N], c;
    int res[N][6];
    
    int getlas(int id){
    	int num = 0;
    	while(id) num++, id>>=1;
    	return n-num+1;
    }
    
    int cnt = 0;
    
    int check(int id){
    	return st.count(id);
    }
    
    int dp(int id, int h) {
    	if(h == 1){
    		
    		int lres = (++sz)%M;
    		if(col.count(id)) {
    			for(int i=0; i<6; i++) r[lres][i] = 0;
    			r[lres][col[id]] = 1;
    			return lres;
    		}else{
    			for(int i=0; i<6; i++) r[lres][i] = 1;
    			return lres;
    		}
    	}
    	
    	if(check(id)){
    		int r1=dp(id*2, h-1), r2=dp(id*2+1, h-1);
    		
    		int lres = (++sz)%M;
    		if(col.count(id)) {
    			int i = col[id];
    			for(int j=0; j<6; j++) r[lres][j]=0;
    			
    			for(int j=0; j<6; j++){
    					for(int k=0; k<6; k++){
    						if(checkColor(i, j) || checkColor(i, k)) continue;
    						r[lres][i] += r[r1][j]*r[r2][k]%mod;
    						r[lres][i] %= mod;
    					}
    			}
    			
    			return lres;
    		}else{
    			for(int j=0; j<6; j++) r[lres][j]=0;
    			for(int i=0; i<6; i++){
    				for(int j=0; j<6; j++){
    					for(int k=0; k<6; k++){
    						if(checkColor(i, j) || checkColor(i, k)) continue;
    						r[lres][i] += r[r1][j]*r[r2][k]%mod;
    						r[lres][i] %= mod;
    					}
    				}
    			}
    			return lres;
    		}
    		
    	}else{
    		int lres = (++sz)%M;
    		for(int i=0; i<6; i++){
    			r[lres][i] = res[h][i];
    		}
    		return lres;
    	}
    }
    
    signed main(){
    	n=read(), m=read();
    	for(int i=1; i<=m; i++){
    		p[i]=read();
    		c=get(readc()); reads();
    		
    		int cur = p[i];
    		while(cur) {
    			if(!st.count(cur)) st.insert(cur);
    			cur /= 2; 
    		} 
    		col[p[i]] = c;
    	}
    	
    	int num = 1;
    	for(int i=1; i<=n; i++){
    		for(int j=0; j<6; j++) res[i][j] = num;
    		num = (num*num%mod*16%mod); 
    	}
    	
    	int x = dp(1, n);
    	int ans = 0;
    	for(int i=0; i<6; i++) ans = (ans + r[x][i]) % mod;
    	cout << ans << endl;
    	
    	return 0;
    }
    
    
  • 相关阅读:
    java 笔记(2) 接口作为引用数据类型
    linux 笔记(5)让vi或vim显示行数和不显示行数
    linux 笔记(4)Ubuntu 使用时vi编辑器时,不能使用backspace键来进行退格或者不能正常使用
    linux 笔记(3)sudo passwd 设置root用户的密码
    matlab笔记(1) 元胞结构cell2mat和num2cell
    linux 笔记(2) 目录直接强行删除rm -rf *(删除当前目录所有的内容)
    linux 笔记(1) ctrl+c,ctrl+z,ctrl+d
    C51单片机项目:红绿灯
    C51单片机项目:时钟
    java 笔记(1)在子类中,一定要访问父类的有参构造方法?
  • 原文地址:https://www.cnblogs.com/ltdjcoder/p/15384292.html
Copyright © 2020-2023  润新知