码迷,mamicode.com
首页 > 其他好文 > 详细

HDU - 5156 Harry and Christmas tree

时间:2015-10-20 13:53:47      阅读:274      评论:0      收藏:0      [点我收藏+]

标签:

 

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

 

题意 :

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

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

 

思路 :

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

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

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

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

 

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

 

题目代码 : 

  1 #include <cstdio>
  2 #include <cstring>
  3 #include <algorithm>
  4 #include <vector>
  5 
  6 using namespace std;
  7 
  8 const int MAXN = 5e4+10;
  9 const int MAXM = 5e5+10;
 10 
 11 vector<int> edge[MAXN];
 12 vector<int> gift[MAXN];
 13 int seq[2*MAXM];
 14 int st[MAXN];
 15 int ed[MAXN];
 16 int s[2*MAXM];
 17 int mp[2*MAXM];
 18 int ans[MAXN];
 19 int pos[MAXN];
 20 int cnt, n, m;
 21 
 22 void Dfs(int u, int fa)
 23 {
 24     st[u] = cnt + 1;
 25     int num = gift[u].size();
 26     for(int i = 0; i < num; i++) {
 27         seq[++cnt] = gift[u][i];
 28     }
 29     int len = edge[u].size();
 30     for(int i = 0; i < len; i++) {
 31         int v = edge[u][i];
 32         if(v != fa) Dfs(v, u);
 33     }
 34     ed[u] = cnt;
 35 }
 36 
 37 int Lowbit(int x)
 38 {
 39     return x & (-x);
 40 }
 41 
 42 void Add(int x, int val)
 43 {
 44     for(int i = x; i <= cnt; i += Lowbit(i)) {
 45         s[i] += val;
 46     }
 47 }
 48 
 49 int Sum(int x)
 50 {
 51     int res = 0;
 52     for(int i = x; i > 0; i -= Lowbit(i)) {
 53         res += s[i];
 54     }
 55     return res;
 56 }
 57 
 58 bool cmp(int a, int b)
 59 {
 60     return ed[a] < ed[b];
 61 }
 62 
 63 void Init()
 64 {
 65     for(int i = 0; i <= n; i++) {
 66         edge[i].clear();
 67         gift[i].clear();
 68     }
 69     for(int i = 1; i <= n; i++) {
 70         pos[i] = i;
 71     }
 72     memset(s, 0, sizeof(s));
 73     memset(mp, 0, sizeof(mp));
 74 }
 75 
 76 int main()
 77 {
 78     int u, v;
 79 
 80     while(scanf("%d %d", &n, &m) != EOF) {
 81         Init();
 82         for(int i = 0; i < n-1; i++) {
 83             scanf("%d %d", &u, &v);
 84             edge[u].push_back(v);
 85             edge[v].push_back(u);
 86         }
 87         while(m--) {
 88             scanf("%d %d", &u, &v);
 89             if(find(gift[u].begin(), gift[u].end(), v) == gift[u].end()) {
 90                 gift[u].push_back(v);
 91             }
 92         }
 93         cnt = 0;
 94         Dfs(1, -1);
 95         sort(pos+1, pos+1+n, cmp);
 96         for(int i = 1; i <= cnt; i++) {
 97             if(mp[seq[i]] == 0) {
 98                 Add(i, 1);
 99                 mp[seq[i]] = i; //如果是第一次出现, mp[seq[i]]记录为当前位置
100             }
101         }
102         int right = 1;
103         for(int i = 1; i <= n; i++) {
104             int now = pos[i];
105             while(right <= ed[now]) {
106                 if(mp[seq[right]] != right) { //如果不是第一次出现
107                     Add(mp[seq[right]], -1);  //减去前一次出现的
108                     Add(right, 1);
109                     mp[seq[right]] = right;   //重新定义这个数最近一次出现位置
110                 }
111                 right++;
112             }
113             ans[now] = Sum(ed[now]) - Sum(st[now] - 1);
114         }
115         printf("%d", ans[1]);
116         for(int i = 2; i <= n; i++) {
117             printf(" %d", ans[i]);
118         }
119         printf("\n");
120     }
121 
122     return 0;
123 }

 

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

 

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

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

这个操作对应的代码

 

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

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

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

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

 

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

 

 

恩, 就是这样...

 

HDU - 5156 Harry and Christmas tree

标签:

原文地址:http://www.cnblogs.com/Quinte/p/4894501.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!