【题解】CF1594E1/E2 Rubik's Cube Coloring (easy&hard version)

同时作为 \(E1\) 题解了。

\(MyBlog\)

题目描述

给定 \(2^n-1\) 个节点的完全二叉树,每个点有六种颜色,颜色有互斥性质,求所有合法的二叉树染色方案。

Easy version

这个比较简单,但我也掉进坑里了)首先先手玩一下,看到样例 \(n=3\) 的时候数字就那么大,绝对有什么次幂性质。

一个点的答案显然是六种。考虑不断向上合并,每一个节点可以选择六种颜色,所以我们考虑一种颜色再乘回去即可。

发现,对于选定一种颜色,两边均有四种颜色可以选,乘起来就是 \(16\) 种,最后的答案必然形如 \(6\times 4^{x}.\) 我们把手推的一些写下来:

\[0,2,6,14,30... \]

到这里规律也就很明显了,也可以给出严谨推导。设左右子树每种颜色的对应方案数为 \(4^x,\) 则对于一种颜色,左侧右侧均有 \(4^{x+1}\) 种可能,乘起来就是 \(4^{2x+2}\) 种。

所以对于 \(4\) 指数上的递推就搞定了。但是注意,对于指数上面的取模,我们要对 \(\varphi(p)=(10^9+6)\) 取模,这里利用欧拉定理

于是简单版本就做完了。

Hard version

树太大了,忍不了,怎么办?

发现有效的点只有两千个,算上把它们全部钩构造成完整结构的也不多,所以直接考虑建立虚树进行 \(dp.\)

显然的性质是只有其祖先有用,而由于是完全二叉树,所以祖先个数只有 \(O(\log n)\) 个,于是点数就可以接受了。

同时我们也发现,这个不同于一般的虚树,这里我们建立一定是要把从这个点到根节点的一整条链全部建立出来的,因为它们都是必须点。所以我们最后要 \(dp\) 的树就是普通的树,但是其所有隐式节点,或者说没有被建立的节点,其值不受固定颜色点的干扰。

考虑普通树怎么 \(dp:\) 设 \(f[x][1..3]\) 表示对应三种不同划分颜色对应的方案数。(三种是因为这题互斥性质两个颜色相对)

那么转移就是直接把两个子树乘起来。

那么现在考虑定初值。初值显然不受其他带颜色点影响,所以直接用第一问的方法,取 \(\frac{1}{6}\) 即可。

知道初值咋求了,这 \(dp\) 的方程也知道了,剩下的就是一个 \(dfs\) 的事情了。

注意用 map<int,int> 一开始有一个数组忘记开 RE 了好几次

#include<bits/stdc++.h>
using namespace std;
typedef double db;
#define int long long
const int mod=1e9+7;
const db eps=1e-10;
inline int Max(int x,int y){return x>y?x:y;}
inline int Min(int x,int y){return x<y?x:y;}
inline db Max(db x,db y){return x-y>eps?x:y;}
inline db Min(db x,db y){return x-y<eps?x:y;}
inline int Add(int x,int y,int M=mod){return (x+y)%M;}
inline int Mul(int x,int y,int M=mod){return 1ll*x*y%M;}
inline int Dec(int x,int y,int M=mod){return (x-y+M)%M;}
inline int Abs(int x){return x<0?-x:x;}
inline int read(){
	int s=0,w=1;
	char ch=getchar();
	while(!isdigit(ch)){
		if(ch=='-')w=-1;
		ch=getchar();
	}
	while(isdigit(ch)){
		s=s*10+ch-'0';
		ch=getchar();
	}
	return s*w;
}
inline void write(int x){
	if(x<0)putchar('-'),x=-x;
	if(x>9)write(x/10);
	putchar(x%10+'0');
}
const int N=2e5+10;
int k,n;
int F[61];
string s;
typedef long long ll;
map<ll,ll>pa,col,lson,rson,f[4];
vector<ll>Node;
inline int qpow(int x,int y){
	int res=1;
	while(y){
		if(y&1)res=Mul(res,x);
		x=Mul(x,x);y>>=1;
	}
	return res;
}
inline int Getdep(int v){return log2(v)+1;}
void Treat(){
	for(auto v:Node){
		if(lson[v]||rson[v])continue;
		int Dep=k-Getdep(v)+1;
		if(col[v])f[col[v]][v]=Mul(1,qpow(4,F[Dep]));
		else for(int i=1;i<=3;++i)f[i][v]=Mul(2,qpow(4,F[Dep]));
	}
}
void DP(ll x){
	if(!lson[x]&&!rson[x])return;
	if(lson[x])DP(lson[x]);
	if(rson[x])DP(rson[x]);
	ll L1=f[1][lson[x]],L2=f[2][lson[x]],L3=f[3][lson[x]],R1=f[1][rson[x]],R2=f[2][rson[x]],R3=f[3][rson[x]];
	if(!lson[x]){L1=L2=L3=Mul(2,qpow(4,F[k-Getdep(x*2)+1]));}
	if(!rson[x]){R1=R2=R3=Mul(2,qpow(4,F[k-Getdep(x*2)+1]));}
	if(!col[x]){
		f[1][x]=Mul(2,Mul(Add(L2,L3),Add(R2,R3)));
		f[2][x]=Mul(2,Mul(Add(L1,L3),Add(R1,R3)));
		f[3][x]=Mul(2,Mul(Add(L2,L1),Add(R2,R1)));
	}
	else{
		if(col[x]==1)f[1][x]=Mul(Add(L2,L3),Add(R2,R3));
		if(col[x]==2)f[2][x]=Mul(Add(L1,L3),Add(R1,R3));
		if(col[x]==3)f[3][x]=Mul(Add(L2,L1),Add(R2,R1));
	}
}
signed main(){
 	freopen("My.out","r",stdin);
	k=read();
	F[1]=0;
	for(int i=2;i<=60;++i){
		F[i]=Add(F[i-1],1,mod-1);
		F[i]=Mul(F[i],2,mod-1);
	}
	n=read();
	for(int i=1;i<=n;++i){
		int pos=read();
		cin>>s;
		if(s=="orange"||s=="red")col[pos]=1;
		if(s=="white"||s=="yellow")col[pos]=2;
		if(s=="blue"||s=="green")col[pos]=3;
		while(pos){
			Node.emplace_back(pos);
			pa[pos]=pos/2;
			if(pos%2==0)lson[pos/2]=pos;
			else rson[pos/2]=pos;
			pos/=2;
		}
	}
	sort(Node.begin(),Node.end());
	ll root=1;
	Treat();
	DP(root);
	int res=Add(f[3][root],Add(f[1][root],f[2][root]));
	write(res);putchar('\n');
	return 0;
}
上一篇:cube.js 基于queryRewrite 进行安全控制


下一篇:Three.js中使用requestAnimationFrame方法实现立方体转动和小球跳动的动画