[BestCoder Round #25 1003]Harry and Christmas tree

Harry and Christmas tree

Time Limit: 5000/2500 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others)

Total Submission(s): 70    Accepted Submission(s): 3

问题描述

圣诞节的夜晚,哈利得到一棵圣诞树。这棵树由n个节点组成,以1号节点为根。树上的每个节点有一些不同颜色的礼物,于是哈利开始统计树上的礼物。哈利每次统计一个礼物,会记录一个(a, b),a表示这个礼物所在的节点,b表示这个礼物的颜色。统计完之后,哈利想知道,每个节点所包含的子树下,有几种不同颜色的礼物。

输入描述

多组输入数据
每组数据第一行由n m组成表示树的大小,以及礼物的个数1≤n≤50000,1≤m≤500000
接下来n-1行,每行两个数a b表示节点(a, b)之间有一条边1≤a,b≤n,a≠b
接下来m行,每行两个数a b表示在a节点上有一个颜色为b的礼物1≤a≤n,1≤b≤100000

输出描述

每组数据输出一行n整数,第i个数表示i节点所包含的子树下有几种不同颜色的礼物。

输入样例

5 3
3 1
4 1
2 3
5 1
1 9
4 6
4 6

输出样例

2 0 0 1 0

提示:
尽可能的让你的程序跑得快一点

读完题的时候没什么思路,暴力一看过不去,在合并子树信息的时候想到要为每种颜色判重。。然后,就不知所措了。。比赛结束后看到了题解,发现真是太神了。

①怎么判重?不判重!为每种颜色单独处理:设我们已经处理好了i个节点,那么我们在处理第i+1个节点的时候,只需要在第i+1个节点上打上1的标记,在第i+1个节点与前i个节点的LCA处打上-1的标记,最后将标记累加即可!

②LCA该怎么求?求出i对节点的LCA?No!

神奇的DFS序!

DFS有一个很好的特性,它在处理一棵子树时,在处理完这棵子树之前不会处理其他的子树;这将作为我们下面证明的引理。

定理:记节点i,j的LCA为(i,j),记节点x的深度为Dx;若将颜色1对应的n个节点以DFS序排序为{a1,a2,a3,...,an},则对任意i必有D(a1,ai)<=D(a2,ai)<=D(a3,ai)<=...<=D(ai-1,ai)(即(ai-1,ai)即为ai与前i-1个节点的LCA,所以我们只需要在(ai-1,ai)上打上-1的标记即可)。

证明:

显然,i∈[2,n]∩N。

①i=2,则只存在(a1,a2)一项,定理成立。

②i>2,若存在j∈[1,i-2]∩N,使得D(aj,ai)>D(aj+1,ai).

则(aj,ai)在以(aj+1,ai)为根的子树中,即aj在以(aj+1,ai)为根的子树中;

且(aj+1,ai)不在以(aj,ai)为根的子树中,

因为若(aj+1,ai)在以(aj,ai)为根的子树中,则D(aj+1,ai)>=D(aj,ai).

即aj+1不在以(aj,ai)为根的子树中。

设以(ai,aj)为根的子树为T,

则DFS访问T时必已经访问过aj+1,或没有访问aj+1。

又∵aj∈T,ai∈T。

若设节点x的DFS序为P(x),

则P(aj+1)>P(ai)>P(aj)或P(aj+1)<P(aj)<P(ai)

但由题中所设,P(aj)<P(aj+1)<P(ai),皆与上述两种情况矛盾。

故不存在j∈[1,i-2]∩N,使得D(aj,ai)>D(aj+1,ai).

即原命题成立。

#include<iostream>
using namespace std;
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<vector>
vector<int> nodegift[50001],sorted[100001];
int fa[50001],n[100001],p[50001],s[100001],ans[50001];
inline void Sort(int x,int ftr){
    int i;
    for(i=nodegift[x].size();i--;)
        sorted[nodegift[x][i]].push_back(x);
    for(i=p[x];i;i=n[i])
        if(s[i]!=ftr)
            Sort(s[i],x);
}
inline int find(int x){
    return fa[x]!=fa[fa[x]]?fa[x]=find(fa[x]):fa[x];
}
vector<int> query[50001];
inline void Tarjan(int x,int ftr){
    int i;

    fa[x]=x;

    for(i=p[x];i;i=n[i])
        if(s[i]!=ftr)
            Tarjan(s[i],x);

    for(i=query[x].size();i--;)
        --ans[find(query[x][i])];

    fa[x]=ftr;
    ans[ftr]+=ans[x];
}
int main(){
    int i,N,m,j,a,b;
    while(~scanf("%d%d",&N,&m)){
        /*----Initialize----*/
        memset(p,0,sizeof(int)*(N+1));
        memset(ans,0,sizeof(int)*(N+1));
        for(i=N;i;--i){
            vector<int>().swap(nodegift[i]);
            vector<int>().swap(query[i]);
        }
        /*------Input-------*/
        for(i=N;--i;){
            scanf("%d%d",&a,&b);
            n[i]=p[a],p[a]=i,s[i]=b;
            n[i+N]=p[b],p[b]=i+N,s[i+N]=a;
        }
        while(m--){
            scanf("%d%d",&a,&b);
            nodegift[a].push_back(b);
        }
        for(i=100000;i;--i)vector<int>().swap(sorted[i]);
        /*-Sort for a Order-*/
        Sort(1,0);
        for(i=100000;i;--i){
            for(j=sorted[i].size();j--;)
                ++ans[sorted[i][j]];
            for(j=sorted[i].size()-1;j>0;--j)
                query[sorted[i][j]].push_back(sorted[i][j-1]);
        }
        /*-Tarjan for anses-*/
        Tarjan(1,0);
        /*-------Output-----*/
        for(i=1;i<N;++i)printf("%d ",ans[i]);
        printf("%d\n",ans[N]);
    }
}

代码之后:

一、

这道题因为先求了一遍DFS序,所以无论是Tarjan特别好写,但我第一遍还是写残了:

③我在清sorted数组的时候忘记了sorted的意义,一不小心把它和其余的数组一起从1~N清了,这显然是不正确的,以后写代码的时候一定要注意这一点:头脑清醒,分清自己给变量设置的意义。

更加错误的是我竟然连数据也没编对拍也没写就交上去了,这实在是兵家之大忌;以后一定要记住了!!!!写完代码之后,不管是多简单的题——第一件事一定是编数据;如果发现能写个暴力什么的话,就写个对拍。

二、借着这道题顺便学了一下DFS序,发现还是很神的,下面是一些学习笔记:

④DFS序可以用来维护树中两点距离,可以用来维护子树权和:

因为在DFS序中,一个子树一定是连成一片的,所以子树求和就化为了区间求和问题;

若在进入节点时写下节点权值和,退出节点时写下节点节点权值和的相反数,则节点和某一以其为根的子树中的节点的距离就可以用它们之间的区间和来表示了,因为不在路径上的点一定在进入之后会被退出,而在路径上的点在进入之后一定不会被退出;这与LCA转RMQ其实有异曲同工之妙。

⑤对于求树的直径

一个强大的贪心:距离树中任意一节点最远的点一定是树的直径的某一端点;

一个靠谱的DP:树的直径一定是由一个点向下的一条最短路和次短路接成。

时间: 2024-10-04 06:53:33

[BestCoder Round #25 1003]Harry and Christmas tree的相关文章

从lca到树链剖分 bestcoder round#45 1003

bestcoder round#45 1003 题,给定两个点,要我们求这两个点的树上路径所经过的点的权值是否出现过奇数次.如果是一般人,那么就是用lca求树上路径,然后判断是否出现过奇数次(用异或),高手就不这么做了,直接树链剖分.为什么不能用lca,因为如果有树退化成链,那么每次询问的复杂度是O(n), 那么q次询问的时间复杂度是O(qn) 什么是树链剖分呢? 就是把树的边分成轻链和重链 http://blogsina.com.cn/s/blog_6974c8b20100zc61.htmlh

BestCoder Round #29 1003 (hdu 5172) GTY&#39;s gay friends [线段树 判不同 预处理 好题]

传送门 GTY's gay friends Time Limit: 6000/3000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others)Total Submission(s): 264    Accepted Submission(s): 57 Problem Description GTY has n gay friends. To manage them conveniently, every morning he o

Bestcoder round #65 &amp;&amp; hdu 5593 ZYB&#39;s Tree 树形dp

Time Limit: 3000/1500 MS (Java/Others)    Memory Limit: 131072/131072 K (Java/Others)Total Submission(s): 354    Accepted Submission(s): 100 Problem Description ZYB has a tree with N nodes,now he wants you to solve the numbers of nodes distanced no m

hdu 5154 Harry and Magical Computer(BestCoder Round #25)

Harry and Magical Computer                                                       Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total Submission(s): 472    Accepted Submission(s): 222 Problem Description In reward

HDU 5806 NanoApe Loves Sequence Ⅱ(尺取+思维)——BestCoder Round #86 1003

传送门 NanoApe Loves Sequence Ⅱ Time Limit: 4000/2000 MS (Java/Others)    Memory Limit: 262144/131072 K (Java/Others)Total Submission(s): 514    Accepted Submission(s): 248 Problem Description NanoApe, the Retired Dog, has returned back to prepare for f

HDU 5778 abs(暴力枚举)——BestCoder Round #85 1003

传送门 abs Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 131072/131072 K (Java/Others)Total Submission(s): 1474    Accepted Submission(s): 511 Problem Description Given a number x, ask positive integer y≥2, that satisfy the following condition

HDU 5682/BestCoder Round #83 1003 zxa and leaf 二分+树

zxa and leaf Problem Description zxa have an unrooted tree with n nodes, including (n−1) undirected edges, whose nodes are numbered from 1 to n. The degree of each node is defined as the number of the edges connected to it, and each node whose degree

Best Coder Round#25 1003 树的非递归访问

虽然官方解释是这题目里的树看作无向无环图,从答案来看还是在“以1作为根节点”这一前提下进行的,这棵树搭建好以后,从叶节点开始访问,一直推到根节点即可——很像动态规划的“自底向上”. 但这棵树的搭建堪忧:给出的边不知道哪边更接近根节点.所以我给出的方案干脆在两个顶点都将对方加成孩子,等到访问的时候再作处理,根据从1这个根节点开始访问这个特性,额外加一个“isVisited"来做区分. 然后利用栈对树进行非递归访问 /** * For best-coder problem 3 */ #include

BestCoder Round#11div2 1003

----- 有时候如果枚举起点超时,那么试试枚举终点. 枚举每一个i为终点(0<= i < n),且维护起点下标startPos 对于终点i,cnt[str[i]] ++,   如果小于等于k的话,那么以i为结尾的符合条件的字串的个数为i-startPos+1 如果大于k的话,改变区间下标startPos, 保证区间startPos-->i内相同字母的个数不超过k 1 while(true) 2 { 3 cnt[str[startPos]] --; 4 if(str[startPos]