题意:
给出一棵树,用m种颜色给树上的每一个节点染色,求不同的染色方案。两个染色方案视为相同,当且仅当子树内的节点经过旋转或不旋转后相同。旋转:这棵树长得非常怪异,在以u为根的子树,u的一代子节点形成了一个类似环状的结构;旋转一个单位后,原来排在第一位的子树排在了最后,原来排在第二的子树排在了第一。
思路:
先简单的介绍一下Polya定理
设
是n个对象的一个置换群,记
为置换
的循环节数, 用m种颜色染这n个对象,则不同的染色方案数为:
当然,如果这并不是一个单纯的染色,可能涉及到一些限制时,你可以将
替代为在置换
下染色的方案数
,由于不同循环间相互独立,而同一循环中的元素染色相同,所以
等于每一个循环的染色方案数的乘积。
我们单独考虑一棵子树,对于这棵子树的一代子节点能产生哪一些置换。首先,肯定能产生不动置换,接下来考虑旋转
个单位后能否产生的置换。若能产生置换,那么在同一个循环中的节点的子树一定具有相同的形态(可以是旋转后具有相同的形态),所以,要处理出tg[u][v],表示子树u与子树v是否具有相同的形态。当我们知道有哪些置换后就可以套用公式了。
代码:
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
#define MAXN 110
#define MO 1000000007
#define LL long long
int c,n,G[MAXN][MAXN],l;
char s[MAXN*20];
LL dp[MAXN],inv[MAXN*MAXN];
void Init(int u)//构造树
{
G[u][0]=0;
while(s[l]=='[')
{
if(s[l]=='[')
{
G[u][++G[u][0]]=++n;
l++,Init(n);
}
l++;
if(s[l]==',') l++;
}
}
int tg[MAXN][MAXN],vis[MAXN];
int check(int u,int v)//检查u,v两棵子树的形态是否相同
{
if(tg[u][v]) return tg[u][v];
if(G[u][0]!=G[v][0]) return tg[u][v]=tg[v][u]=-1;
if(G[u][0]==0) return tg[u][v]=tg[v][u]=1;
for(int i=0;i<G[u][0];i++)
{
bool f=1;
for(int j=1;j<=G[v][0];j++)
{
int k=i+j;
if(k>G[v][0]) k-=G[v][0];
if(check(G[u][k],G[v][j])==-1)
{
f=0;
break;
}
}
if(f==1) return tg[u][v]=tg[v][u]=1;
}
return tg[u][v]=tg[v][u]=-1;
}
void Solve(int u)
{
dp[u]=(u==1)?1:c;
for(int i=1;i<=G[u][0];i++)
{
Solve(G[u][i]);
dp[u]*=dp[G[u][i]];
dp[u]%=MO;
}
int cnt=1;//记录有多少个置换
for(int i=1;i<G[u][0];i++)//枚举旋转多少个单位
{
memset(vis,0,sizeof vis);
bool flag=1;
LL num=c;
for(int j=1;j<=G[u][0];j++)
if(!vis[j])
{
int k=j;
do
{
vis[k]=1;
if(check(G[u][k],G[u][j])==-1)
{
flag=0;
break;
}
k+=i;
if(k>G[u][0]) k-=G[u][0];
}while(!vis[k]);
if(flag==0) break;
num*=dp[G[u][j]];
num%=MO;
}
if(flag)
{
cnt++;
dp[u]=(dp[u]+num)%MO;
}
}
dp[u]=dp[u]*inv[cnt]%MO;
}
int main()
{
inv[1]=1;
for(int i=2;i<MAXN*MAXN;i++)
inv[i]=1LL*(MO-MO/i)*inv[MO%i]%MO;
int tm;
scanf("%d",&tm);
for(int cs=1;cs<=tm;cs++)
{
memset(tg,0,sizeof tg);
scanf("%s%d",s,&c);
n=0,l=0;
Init(++n);
Solve(1);
printf("Case #%d: %d\n",cs,dp[1]);
}
}