COGS 2096. 不平凡的许愿树

【题目描述】


noip要到了,大家来到许愿树前。这个许愿树不仅仅是许愿树,还有未卜先知的功能。众OIer问许愿树:“不平凡的许愿树,CCF告诉我们noip中会有两道题目从Openjudge上选择,你能不能告诉我是哪两道题。”

许愿树想了想直接说出答案并不妥:“中国有句古话叫‘闷声大发财’,我就什么也不说,这是最好的。但是我看到你们这么热情,一句话不说也不好,我就告诉你 们点信息吧。你们看我是一个由N个结点组成的树,在树中任选着3个点,有多少种选择方案使得这三个点互相之间的距离相同?两个方案不同当且仅当一个点在第 一种方案中被选择,第二种方案中没有被选择。”

“记你算出来方案数为cnt,那么第一道题的题号就是cnt%338 + 1,第二题的题目编号是(cnt+233)%338+1。”

可是OIer们手头并没有计算机,于是请你来告诉他们题目编号。


【输入格式】


第一行一个整数N,表示树有N个点。

接下来N-1行,每行两个整数u,v,表示树中有一条从u到v的边


【输出格式】

一行,两个整数,分别为预测的第一题题号和第二题题号。

【样例输入】

7
1 2
5 7
2 5
2 3
5 6
4 5

【样例输出】

6 239

【提示】

样例解释:


共有5种方案,分别是{1,3,5},{2,4,6},{2,4,7},{2,6,7},{4,6,7}。所以第一题的编号为5%338 + 1 = 6;第二题的编号为(5+233)%338 + 1 = 239;

数据范围与约定:

对于30%的数据:1 <= n <= 100

对于60%的数据:1 <= n <= 1500

对于100%的数据:1 <= n <= 5000

胡扯:

其实Openjudge没有确切题号,第1.1节有10题,第1.2节有10题...,不如约定第16题的编号是第1.2节的第6题。如果命中我什么都不知道。

题解:

  这个题目思路还是比较清晰的。首先我们要知道一个结论,显然树上三点相互之间的最短路径的焦点有且只有一个。

  那么这个题目就比比较好做了,我们可以枚举那个中心点然后算总方案数-不合法的方案数。

  具体怎么做呢?可以考虑枚举每个点,以他为根dfs,求出每一层的点数(深度相同),然后显然总方案数等于所有的点在每一层选三个的组合数,相加。当然有不和法的情况:1.如果选中的三个点在同一棵子树里面的话那么显然就不和法。2.如果在一棵子数里面选了两个点,在另一棵子树又选一个点那么也是不合法的。那么我们每次dfs每个子树算一下对应的组合数就可以了。

代码:

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <iostream>
#include <queue>
#define ll long long
#define MAXN 5010
#define mod 338 
using namespace std;
struct edge{
    int first;
    int next;
    int to;
}a[MAXN*2];
int c[MAXN][MAXN],dep[MAXN],dis[MAXN];
int n,num=0;
ll ans=0;

inline void addedge(int from,int to){
    a[++num].to=to;
    a[num].next=a[from].first;
    a[from].first=num;
}

inline void dfs(int now,int f,int d,int *dep){
    ++dep[d];
    for(int i=a[now].first;i;i=a[i].next){
        int to=a[i].to;
        if(to==f) continue;
        dfs(to,now,d+1,dep);
    }
}

inline void pre(){
    c[0][0]=1;
    for(int i=1;i<=n;i++){
        c[i][0]=1;
         for(int j=1;j<=i;j++) c[i][j]=(c[i-1][j-1]+c[i-1][j])%mod;
    }
}

int main()
{
    scanf("%d",&n);
    for(int i=1;i<n;i++){
        int x,y;scanf("%d%d",&x,&y);
        addedge(x,y);
        addedge(y,x);
    }
    pre();
    for(register int now=1;now<=n;now++){
        for(register int i=1;i<=n;i++) dep[i]=0;
        dfs(now,0,0,dep);
        for(register int k=1;k<=n;k++) ans+=c[dep[k]][3]%=mod;
        for(int i=a[now].first;i;i=a[i].next){
            int to=a[i].to;
            for(register int i=1;i<=n;i++) dis[i]=0;
            dfs(to,now,1,dis);
            for(register int k=1;k<=n;k++) ans+=mod-(c[dis[k]][3]+c[dis[k]][2]*(dep[k]-dis[k]))%mod;
            if(ans>=mod) ans-=mod;
        }
    }
    printf("%lld %lld",ans%mod+1,(ans+233)%mod+1);
    return 0;
}
原文地址:https://www.cnblogs.com/renjianshige/p/7419622.html