往一颗树上标号 要求同一父亲节点的节点们标号连续 同一子树的节点们标号连续 问一共有几种标法
画了一画 发现标号有二叉树的感觉
初始标号1~n 根结点1可以标1或n 否则其他情况无法让下面的子树满足各自连续并且该根的儿子节点都要连续
根结点下的节点平分其他标号 画一画可以发现 每个根下最多有两颗子树 否则无法满足条件 并且两颗子树占据剩余标号的左右两边 中间夹的必须是叶子 这样才能满足该根下的儿子节点标号连续
若根下只有一颗子树 同样可以选择占剩余标号左部分/右部分
剩余叶子全排列乘上即可 每个根都这样遍历一遍 如果期间出现一个根下有两颗以上的子树 就没法标号 即方案数为0 否则遍历完输出方案数即可
代码如下:
#include <iostream>
#include <cmath>
#include <queue>
#include <cstdio>
#include <cstring>
#include <vector>
#define ll long long
#define MOD 1000000007
using namespace std;
ll a[100010]={1,1,2};//A(n,n)排列组合
int n;
vector<int> s[100010];
bool vis[100010];
ll bfs()
{
memset(vis,0,sizeof(vis));
queue <int> q;
q.push(1);
vis[1] = 1;
ll ans = 2;
int i,u,v,yz,gen,sz;
while(!q.empty())
{
u = q.front();
q.pop();
yz = gen = 0;
sz = s[u].size();
for(i = 0; i < sz; ++i)
{
v = s[u][i];
if(vis[v]) continue;
if(s[v].size() == 1)//当前节点为叶子节点(只有v-u一条边)
{
yz++;
}
else//为根结点
{
gen++;
q.push(v);
}
vis[v] = 1;
}
if(gen > 2) return 0;//根结点超2 无解
else if(gen)
{
ans = ((ans*2)%MOD*a[yz])%MOD;
}
else ans = (ans*a[yz])%MOD;
}
return ans;
}
int main()
{
for(int i=3;i<=100001;i++)
a[i]=(a[i-1]*i)%MOD;
int t,k=0;
scanf("%d",&t);
while(k++,t--)
{
scanf("%d",&n);
memset(s,0,sizeof(s));
int u,v;
for(int i=1;i<n;i++)//双向建树
{
scanf("%d %d",&u,&v);
s[u].push_back(v);
s[v].push_back(u);
}
if(n == 1) printf("Case #%d: 1\n",k);//特判只有树根的情况
else printf("Case #%d: %I64d\n",k,bfs());
}
return 0;
}
版权声明:本文为博主原创文章,未经博主允许不得转载。
时间: 2024-11-05 02:22:04