【题】【搜索+数学】NKOJ3867 宾馆

17 篇文章 0 订阅
7 篇文章 0 订阅

NKOJ3867 宾馆
时间限制 : - MS 空间限制 : 165536 KB
评测说明 : 1s

问题描述
何老板开了一间宾馆,共n个房间,通过n-1双向道路相连,每条道路的长度相同,任意两个房间都有且仅有一条路径可以到达。有三名同行的顾客需要各开一个房间。三个客人要求住的房间要互不相同,且三个房间两两距离相同。
何老板想知道,有多少种方案能让他们满意?

输入格式
第一行一个数n。
接下来n-1行,每行两个数x,y,表示x和y之间有一条道路相连。

输出格式
一个整数,表示满足要求的方案数。

样例输入 1
7
1 2
5 7
2 5
2 3
5 6
4 5

样例输出 1
5

样例输入 2
20
1 2
2 3
1 4
3 5
2 6
4 7
6 8
4 9
7 10
10 11
9 12
3 13
9 14
5 15
2 16
6 17
5 18
11 19
4 20

样例输出 2
18

提示
【样例1说明】
{1,3,5},{2,4,6},{2,4,7},{2,6,7},{4,6,7}
【数据范围】
对于30%的数据 1≤N≤100
对于100%的数据 1≤N≤5000

来源 改编自Poi2014 hotel

思路:三个房间两两间路径一定会经过一个中心点,所以依次枚举中心点。
对于某中心点z,再依次枚举各层子孙所能做出的贡献。
枚举子孙时,第一层,为儿子数选3个的组合。以后的每一层,暴力计算贡献数量。
注意:
剪枝1:枚举到某一层时,该子树已没有节点,以后都不枚举该子树了。
剪枝2:若该层有节点的子树小于3,也没有必要加深了

#include<cstdio>
#include<iostream>
#include<cmath>
#include<bitset>
#include<vector>
using namespace std;
const int need=5003;

int n;
vector<int> w[need];
typedef vector<int>::iterator iit;
//.................................................
inline void in_(int &d)
{
    char t=getchar();
    while(t<'0'||t>'9') t=getchar();
    for(d=0;!(t<'0'||t>'9');t=getchar()) d=(d<<1)+(d<<3)+t-'0';
}
//.................................................
long long c(const int &n,int m=3)
{
    if(m>n) return 0;
    long long c[2]={1,};
    for(int i=1;i<=m;i++) c[i&1]=c[(i+1)&1]*(n-i+1)/i;
    return c[m&1];
}

int cnt_son(const int &fa,const int &k,int m)
{
    if(m==1) return w[k].size()-1;
    int son=0;
    for(iit it=w[k].begin();it!=w[k].end();it++)
    {
        if(*it!=fa)son+=cnt_son(k,*it,m-1);
    }
    return son;
}
long long cnt(const int &k)
{
    bitset<need> bt; 
    long long ans=c(w[k].size());
    if(ans==0) return 0;
    int son,tmp;
    long long ctmp;
    iit it,jt,kt;
    for(int i=1;i<=5000;i++)
    {
        son=tmp=0,ctmp=1;
        vector<int> ve;
        for(it=w[k].begin();it!=w[k].end();it++)
        {
            if(bt[*it]) continue;
            tmp=cnt_son(k,*it,i);
            if(tmp) 
            {
                if(ve.size()==2)
                {
                    ctmp=ve[0]*ve[1]*tmp;
                }
                else if(ve.size()>2)
                {
                    for(jt=ve.begin();jt<ve.end();jt++)
                    {
                        for(kt=jt+1;kt<ve.end();kt++)
                        {
                            if(kt==jt) continue;
                            ctmp+=tmp*(*jt)*(*kt);
                        }
                    }   
                }
                ve.push_back(tmp);
            }
            else bt[*it]=1;
        }
        if(ve.size()>=3)ans+=ctmp;
        else return ans;
    }
}
//.................................................
int main()
{
    scanf("%d",&n);
    for(int i=1,a,b;i<n;i++)
    {
        in_(a),in_(b);
        w[a].push_back(b);
        w[b].push_back(a);
    }
    long long ans=0;
    for(int i=1;i<=n;i++) ans+=cnt(i);
    cout<<ans;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值