[未完待续][NOI2017模拟]谈笑风生

题目背景
2016.05.19 T2

题目描述
设 T 为一棵有根树,我们做如下的定义:
设 a 和 b 为 T 中的两个不同节点。如果 a 是 b 的祖先,那么称 “ a 比 b 不知道高明到哪里去了 ” 。
设 a 和 b 为 T 中的两个不同节点。如果 a 与 b 在树上的距离不超过某个给定常数 x ,那么称 “ a 与 b 谈笑风生 ” 。
给定一棵 n 个节点的有根树 T ,节点的编号为 1~n ,根节点为 1 号节点。你需要回答 q 个询问,询问给定两个整数 p 和 k ,问有多少个有序三元组 (a,b,c) 满足:
1. a、b 和 c 为 T 中三个不同的点,且 a 为 p 号节点;
2. a 和 b 都比 c 不知道高明到哪里去了;
3. a 和 b 谈笑风生。这里谈笑风生中的常数为给定的 k 。

输入格式
输入的第一行含有两个正整数n和q,分别代表有根树的点数与询问的个数。
接下来 n-1 行,每行描述一条树上的边。每行含有两个整数 u 和 v ,代表在节点 u 和 v 之间有一条边。
接下来 q 行,每行描述一个操作。第 i 行含有两个整数,分别表示第 i 个询问的 p 和 k 。

输出格式
输出 q 行,每行对应一个询问,代表询问的答案。

样例数据:
输入
5 3
1 2
1 3
2 4
4 5
2 2
4 1
2 3
输出
3
1
3

备注
【样例解释】
样例中的树如下图所示:
这里写图片描述
对于第一个和第三个询问,合法的三元组有 (2,1,4)、(2,1,5) 和 (2,4,5)。
对于第二个询问,合法的三元组只有 (4,2,5) 。

【数据规模与约定】
所有测试点的数据规模如下:
这里写图片描述
对于全部测试数据的所有询问,1≤p≤n,1≤k≤n。

分析: 吐槽一句,这次的题目让我感到好沧桑,无论生理上(-1s)还是心理上(orz做不起),暂时看着吧……(假装看懂了的样子)
这里写图片描述
代码:
30%(本人考场作,暴力dfs)

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<string>
#include<ctime>
#include<cmath>
#include<algorithm>
#include<cctype>
#include<iomanip>
#include<queue>
#include<set>
using namespace std;

int getint()
{
    int sum=0,f=1;
    char ch;
    for(ch=getchar();(ch<'0'||ch>'9')&&ch!='-';ch=getchar());
    if(ch=='-')
    {
        f=-1;
        ch=getchar();
    }
    for(;ch>='0'&&ch<='9';ch=getchar())
        sum=(sum<<3)+(sum<<1)+ch-48;
    return sum*f;
}

int n,q,p,k,tot;
long long ans;
int first[300010],nxt[600010],to[600010];
int dep[300010],son[300010];

int dfs(int root,int deep,int p)//找每个节点的所有儿子计数
{
    dep[root]=deep;
    for(p=first[root];p;p=nxt[p])
        if(dep[to[p]]==-1)
        {
            son[root]=son[root]+1+dfs(to[p],deep+1,root);
        }

    return son[root];   
}

void dfs3(int x,int num)//不要问我为什么是dfs3,因为dfs2最后被删掉了
{
    for(int p=first[x];p;p=nxt[p])
    {
        int v=to[p];
        if(dep[v]>dep[x]&&num<=k)
        {
            ans+=son[v];
            if(num<k)
                dfs3(v,num+1);
        }
    }


}

void add(int x,int y)
{
    tot++;
    nxt[tot]=first[x];
    first[x]=tot;
    to[tot]=y;
}

int main()
{
    freopen("laugh.in","r",stdin);
    freopen("laugh.out","w",stdout);

    memset(dep,-1,sizeof(dep));
    n=getint();q=getint();
    for(int i=1;i<n;++i)
    {
        int x=getint();int y=getint();
        add(x,y);
        add(y,x);
    }

    dfs(1,0,0);

    while(q--)
    {
        p=getint();k=getint();
        ans+=min(dep[p],k)*son[p];//先是与它距离小于k的祖先肯定全部与它“谈笑风生”,而它的每个儿子都满足“矮到不知道哪里去了”

        dfs3(p,1);//找它与它距离小于k的儿子,它能和所有这种儿子“谈笑风生”,而这种儿子的儿子都满足“矮到不知道哪里去了”

        cout<<ans<<endl;
        ans=0;
    }

    return 0;
}

100%

//无orz

本题结?No!No!No!没弄懂怎么能叫结呢?

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值