題目鏈接
題目思路
大佬的一句話 只考慮欽定的點連成樹然后dp
其實就是每個點和根節點連邊,那么只考慮那條鏈上的所有節點
最多\(n*k\)個點
\(dp[i][node]\)表示\(node\)節點顏色為\(i\)的方案數
注意\(num[i]\)數組不要提前取模,對於\(a^b\)的形勢一定要小心取模,找了一年bug
代碼
#include<bits/stdc++.h>
#define pii pair<int,int>
#define fi first
#define se second
#define debug cout<<"I AM HERE"<<endl;
using namespace std;
typedef long long ll;
const int maxn=1e6+5,inf=(1ll<<31)-1,mod=1e9+7;
const double eps=1e-6;
int k,n;
char s[20];
map<ll,ll> col,sz,dp[10];
ll num[70];
ll qpow(ll a,ll b){
ll ans=1,base=a;
while(b){
if(b&1) ans=ans*base%mod;
base=base*base%mod;
b=b>>1;
}
return ans;
}
void dfs(ll node,int dep){
if(sz[node]==0){
// dep+1到k層
for(int i=1;i<=6;i++){
if(k==dep){
dp[i][node]=1;
}else{
dp[i][node]=qpow(4,num[k-dep]);
}
}
return ;
}
if(dep==k){
for(int i=1;i<=6;i++){
if(col[node]==0||i==col[node]){
dp[i][node]=1;
}else{
dp[i][node]=0;
}
}
return ;
}
dfs(node<<1,dep+1);
dfs(node<<1|1,dep+1);
for(int i=1;i<=6;i++){
ll sum0=0,sum1=0;
for(int j=1;j<=6;j++){
if(i%2==1){
if(j!=i&&j!=i+1){
sum0+=dp[j][node<<1];
sum1+=dp[j][node<<1|1];
}
}else{
if(j!=i&&j!=i-1){
sum0+=dp[j][node<<1];
sum1+=dp[j][node<<1|1];
}
}
}
sum0%=mod,sum1%=mod;
// 沒有強制給他顏色,或者強制給的顏色就是i顏色
if(col[node]==0||col[node]==i){
dp[i][node]=sum0*sum1%mod;
}
}
}
signed main(){
// 不要提起對num[i]取模 因為a^b!=a^(b%mod)
for(ll i=1;i<=60;i++){
num[i]=(num[i-1]+(1ll<<i));
}
scanf("%d%d",&k,&n);
for(int i=1;i<=n;i++){
ll x;
scanf("%lld %s",&x,s+1);
if(s[1]=='w') col[x]=1;
if(s[1]=='y') col[x]=2;
if(s[1]=='g') col[x]=3;
if(s[1]=='b') col[x]=4;
if(s[1]=='r') col[x]=5;
if(s[1]=='o') col[x]=6;
while(x){// 標記路徑
sz[x]=1;
x=x/2;
}
}
dfs(1,1);
ll pr=0;
for(int i=1;i<=6;i++){
pr=(pr+dp[i][1])%mod;
}
printf("%lld\n",pr);
return 0;
}