题目链接 : http://acm.hdu.edu.cn/showproblem.php?pid=5156

题意 :

给一颗编号为1-n的以1为根的树, 已知有m个颜色的礼物分布在某些节点上(同一节点可以有多个),

问 : 对于编号从1-n的节点, 每一个节点对应子树上有多少颜色不同的礼物.

思路 :

一开始的想法是DFS记录节点序列, 再开vector记录每个节点上挂的礼物(同一节点上对颜色去重), 用树状数组统计一个区间内不同颜色的种类

但是由于记录位置的数组同样要开到二维, 超过了限制, 于是也开成vector, 果断超时

后来还是看discuss里边sxbk同学的代码才知道更好的解法

DFS记录序列不是节点序列, 而是将所有节点的全部颜色(已去重)都记录在序列内, 这样位置记录的数组可以只用一维

这道题收获挺大的, 对DFS序的思想理解深入了, 同时学习了  统计一个区间内有多少不同的数  这个问题的解法

题目代码 :

 #include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector> using namespace std; const int MAXN = 5e4+;
const int MAXM = 5e5+; vector<int> edge[MAXN];
vector<int> gift[MAXN];
int seq[*MAXM];
int st[MAXN];
int ed[MAXN];
int s[*MAXM];
int mp[*MAXM];
int ans[MAXN];
int pos[MAXN];
int cnt, n, m; void Dfs(int u, int fa)
{
st[u] = cnt + ;
int num = gift[u].size();
for(int i = ; i < num; i++) {
seq[++cnt] = gift[u][i];
}
int len = edge[u].size();
for(int i = ; i < len; i++) {
int v = edge[u][i];
if(v != fa) Dfs(v, u);
}
ed[u] = cnt;
} int Lowbit(int x)
{
return x & (-x);
} void Add(int x, int val)
{
for(int i = x; i <= cnt; i += Lowbit(i)) {
s[i] += val;
}
} int Sum(int x)
{
int res = ;
for(int i = x; i > ; i -= Lowbit(i)) {
res += s[i];
}
return res;
} bool cmp(int a, int b)
{
return ed[a] < ed[b];
} void Init()
{
for(int i = ; i <= n; i++) {
edge[i].clear();
gift[i].clear();
}
for(int i = ; i <= n; i++) {
pos[i] = i;
}
memset(s, , sizeof(s));
memset(mp, , sizeof(mp));
} int main()
{
int u, v; while(scanf("%d %d", &n, &m) != EOF) {
Init();
for(int i = ; i < n-; i++) {
scanf("%d %d", &u, &v);
edge[u].push_back(v);
edge[v].push_back(u);
}
while(m--) {
scanf("%d %d", &u, &v);
if(find(gift[u].begin(), gift[u].end(), v) == gift[u].end()) {
gift[u].push_back(v);
}
}
cnt = ;
Dfs(, -);
sort(pos+, pos++n, cmp);
for(int i = ; i <= cnt; i++) {
if(mp[seq[i]] == ) {
Add(i, );
mp[seq[i]] = i; //如果是第一次出现, mp[seq[i]]记录为当前位置
}
}
int right = ;
for(int i = ; i <= n; i++) {
int now = pos[i];
while(right <= ed[now]) {
if(mp[seq[right]] != right) { //如果不是第一次出现
Add(mp[seq[right]], -); //减去前一次出现的
Add(right, );
mp[seq[right]] = right; //重新定义这个数最近一次出现位置
}
right++;
}
ans[now] = Sum(ed[now]) - Sum(st[now] - );
}
printf("%d", ans[]);
for(int i = ; i <= n; i++) {
printf(" %d", ans[i]);
}
printf("\n");
} return ;
}

另外第一次学习到  统计一个区间内有多少不同的数  这个问题的解法

基本思路是树状数组, 但是有重复的数, 要保证在一个区间内只更新过一次

所做的处理是用先遍历记录数字的数组a, 用数-位置数组 mp[a[i]] 来记录每个数第一次出现的位置并该点更新

这个操作对应的代码

 for(int i = ; i <= n; i++) {
if(mp[a[i]] == ) { //如果是第一次出现, 记录第一次出现位置并更新
Add(i, );
mp[a[i]] = i;
}
}

记录左右查询, 每个查询按区间右端点R[i]从小到大排序

设一个扫描线k, 它的目的保证R[i]之前所有不同的点只更新过一次, 并且是在离R[i]最近的那个点更新

如此便可以写出这样一段代码

 for(int i = ; i <= query_num; i++) {
while(k <= R[i]) {
if(mp[a[i]] != k) { //如果不是最新次出现
Add(mp[a[k]], -); //将上一次出现的更新-1
Add(k, );   //将这个位置新出现的更新1
mp[a[k]] = k; //更新这个数最近一次的位置
}
k++;
}
ans[i] = Sum(ed[i]) - Sum(st[i] - );
}

恩, 就是这样...

HDU - 5156 Harry and Christmas tree的更多相关文章

  1. POJ3013 Big Christmas Tree[转换 最短路]

    Big Christmas Tree Time Limit: 3000MS   Memory Limit: 131072K Total Submissions: 23387   Accepted: 5 ...

  2. poj 3013 Big Christmas Tree (最短路径Dijsktra) -- 第一次用优先队列写Dijsktra

    http://poj.org/problem?id=3013 Big Christmas Tree Time Limit: 3000MS   Memory Limit: 131072K Total S ...

  3. poj 3013 Big Christmas Tree Djistra

    Big Christmas Tree 题意:图中每个节点和边都有权值,图中找出一颗树,树根为1使得 Σ(树中的节点到树根的距离)*(以该节点为子树的所有节点的权值之和) 结果最小: 分析:直接求出每个 ...

  4. hdu 4912 Paths on the tree(树链拆分+贪婪)

    题目链接:hdu 4912 Paths on the tree 题目大意:给定一棵树,和若干个通道.要求尽量选出多的通道,而且两两通道不想交. 解题思路:用树链剖分求LCA,然后依据通道两端节点的LC ...

  5. POJ 3013 Big Christmas Tree(最短Dijkstra+优先级队列优化,SPFA)

    POJ 3013 Big Christmas Tree(最短路Dijkstra+优先队列优化,SPFA) ACM 题目地址:POJ 3013 题意:  圣诞树是由n个节点和e个边构成的,点编号1-n. ...

  6. POJ Big Christmas Tree(最短的基础)

    Big Christmas Tree 题目分析: 叫你构造一颗圣诞树,使得 (sum of weights of all descendant nodes) × (unit price of the ...

  7. poj 3013 Big Christmas Tree

    Big Christmas Tree Time Limit: 3000MS   Memory Limit: 131072K Total Submissions: 20974   Accepted: 4 ...

  8. Big Christmas Tree(poj-3013)最短路

    Big Christmas Tree Time Limit: 3000MS   Memory Limit: 131072K Total Submissions: 25823   Accepted: 5 ...

  9. (hdu)5423 Rikka with Tree (dfs)

    题目链接:http://acm.split.hdu.edu.cn/showproblem.php?pid=5423 Problem Description As we know, Rikka is p ...

随机推荐

  1. HDU 2896 AC自动机 裸题

    中文题题意不再赘述 注意字符范围是可见字符,从32开始到95 char c - 32 #include <stdio.h> #include <string.h> #inclu ...

  2. python django model类型摘要

    V=models.CharField(max_length=None[, **options]) #varchar V=models.EmailField([max_length=75, **opti ...

  3. 使用CSS为内容设定特定的鼠标样式(cursor)介绍

    相信大家都知道我们的鼠标在网页中不同的元素中有不同的显示(例如 a 元素就显示为“箭头指针”),但是其实我们还可以自定义这些有趣的东西哦!今天“畅想资源”就来教大家如何使用CSS为内容设定特定的鼠标样 ...

  4. Mybatis插入语句useGeneratedKeys="true"的用法

    <!-- 插入新的问题件 --> <!-- useGeneratedKeys="true"把新增加的主键赋值到自己定义的keyProperty(id)中 --&g ...

  5. POJ 2976 Dropping tests 01分数规划

    给出n(n<=1000)个考试的成绩ai和满分bi,要求去掉k个考试成绩,使得剩下的∑ai/∑bi*100最大并输出. 典型的01分数规划 要使∑ai/∑bi最大,不妨设ans=∑ai/∑bi, ...

  6. 【iOS之轮播视图、自定义UIPageControl】

    基于UISrollView实现的无限循环轮播视图. 实现的思路:使用三个UIImageView不断循环利用,始终将最中间一个View显示在UIScrolView的contentSize上,每次滚动后, ...

  7. Java多线程——线程池

    系统启动一个新线程的成本是比较高的,因为它涉及到与操作系统的交互.在这种情况下,使用线程池可以很好的提供性能,尤其是当程序中需要创建大量生存期很短暂的线程时,更应该考虑使用线程池. 与数据库连接池类似 ...

  8. SpringMVC06以对象的方式获取前台的数据

    ========创建需要的两个实体类================ public class School { private String sName; private String addres ...

  9. java中Class.forName与new

    一.使用Class.forName 1.装载类 Class clazz = Class.forName("xx.xx.xx"); 2.初始化对象 clazz.newInstance ...

  10. 设计模式:模版模式(Template Pattern)

    android中的Activity框架,View框架中大量的on函数基本上都应用到了Template模式,掌握这一模式对于理解这些框架大有裨益. 模版模式 又叫模板方法模式,在一个方法中定义一个算法的 ...