同時作爲 \(E1\) 題解了。
題目描述
給定 \(2^n-1\) 個節點的完全二叉樹,每個點有六種顏色,顏色有互斥性質,求所有合法的二叉樹染色方案。
Easy version
這個比較簡單,但我也掉進坑裏了)首先先手玩一下,看到樣例 \(n=3\) 的時候數字就那麼大,絕對有什麼次冪性質。
一個點的答案顯然是六種。考慮不斷向上合併,每一個節點可以選擇六種顏色,所以我們考慮一種顏色再乘回去即可。
發現,對於選定一種顏色,兩邊均有四種顏色可以選,乘起來就是 \(16\) 種,最後的答案必然形如 \(6\times 4^{x}.\) 我們把手推的一些寫下來:
到這裏規律也就很明顯了,也可以給出嚴謹推導。設左右子樹每種顏色的對應方案數爲 \(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;
}