Loading

POJ 3321:Apple Tree + HDU 3887:Counting Offspring(DFS序+树状数组)

http://poj.org/problem?id=3321

http://acm.hdu.edu.cn/showproblem.php?pid=3887

POJ 3321:

题意:给出一棵根节点为1的边不一定的树,然后给出问题:询问区间和 或者 节点值更新。

HDU 3887:

题意:和POJ 3321的题意差不多,只不过对每个节点询问不包含该节点的区间和


思路:今天才学了下才知道有DFS序这种东西,加上树状数组处理一下区间和 和 节点更新。


DFS序大概就是我们在DFS遍历一棵树的时候,在进入这个节点的时候,我们用一个变量记录此时的时间(代码中用cnt表示,也是可以看做是目前已经遍历过的节点的数目),
然后分别用一个数组表示开始搜索这个节点的时间,和离开这个节点的时间(代码中用st代表开始,用ed代表结束)。然后我们可以发现在这段时间里面遍历过的节点都是该节点的儿子,即例如HDU3887这组样例:


15 7
7 10
7 1
7 9
7 3
7 4
10 14
14 2
14 13
9 11
9 6
6 5
6 8
3 15
3 12
0 0


我们的遍历顺序是这样的:7 1 1 3 15 15 12 12 3 4 4 10 14 2 2 13 13 14 10 9 11 11 6 5 5 8 8 6 9 7(手打的。。)
分别对应的下标是这样的:例如根节点7,它管辖的范围从前到后全部是,说明它就是根节点了。那么它的范围是【1,30】。

 1 int cnt = 0;
 2 void dfs(int u, int fa)
 3 {
 4     que[++cnt] = u;
 5     for(int k = head[u]; ~k; k = edge[k].nxt){
 6         int v = edge[k].v;
 7         if( v == fa ) continue;
 8         dfs(v, u);
 9     }
10     que[++cnt] = u;
11 }
12 dfs(7, -1);
我这里用的是前向星存的图,然后从根节点7开始遍历。
我先用一个数组来装遍历过的点的顺序,即que数组为我上面列出来的那个序列。
然后我们就按下面这样处理
1 for(int i = 1; i <= cnt; i++) {
2     if( st[que[i]] == 0 ) st[que[i]] = i;
3     else se[que[i]] = i;
4 }
把开始的位置和结束的位置分别装在st数组和ed数组中。
然后我们就可以根据这些条件建立起树状数组了。
询问的区间和为Query( ed[node] ) - Query( st[node] - 1 ) / 2(因为我们重复计数了,每个节点枚举了两次) ,更新就是Update( ed[node], value ); Update( st[node], value );
下面给出HDU 3887 的代码。
  1 #include <cstdio>
  2 #include <algorithm>
  3 #include <cstring>
  4 #include <iostream>
  5 #include <vector>
  6 using namespace std;
  7 #define N 100010
  8 
  9 int st[N], se[N], ans[N], cnt;
 10 int bit[2*N],que[2*N];
 11 int head[N], tot;
 12 struct node
 13 {
 14     int v, nxt;
 15 }edge[N*2];
 16 //vector <int> edge[N];
 17 //dfs序 + 树状数组
 18 void addedge(int u, int v)
 19 {
 20     edge[tot].v = v;
 21     edge[tot].nxt = head[u];
 22     head[u] = tot++;
 23 }
 24 
 25 void dfs(int u, int fa)
 26 {
 27     que[++cnt] = u;
 28     for(int k = head[u]; ~k; k = edge[k].nxt){
 29         int v = edge[k].v;
 30         if( v == fa ) continue;
 31         dfs(v, u);
 32     }
 33     que[++cnt] = u;
 34 }
 35 
 36 int lowbit(int x)
 37 {
 38     return x & (-x);
 39 }
 40 
 41 void Update(int x, int value)
 42 {
 43     while( x <= cnt ) {
 44         bit[x] += value;
 45         x += lowbit(x);
 46     }
 47 }
 48 
 49 int Query(int x)
 50 {
 51     int ans = 0;
 52     while( x > 0 ) {
 53         ans += bit[x];
 54         x -= lowbit(x);
 55     }
 56     return ans;
 57 }
 58 
 59 int main()
 60 {
 61     int n,p;
 62     while(scanf("%d%d", &n, &p), n + p) {
 63 
 64         memset(head, -1, sizeof(head));
 65         tot = 0;
 66 
 67         memset(bit,0,sizeof(bit));
 68         memset(st,0,sizeof(st));
 69 //        for(int i = 1; i <= n; i++)
 70 //            edge[i].clear();
 71         for(int i = 1; i < n; i++) {
 72             int u, v;
 73             scanf("%d%d", &u, &v);
 74             addedge(u, v);
 75             addedge(v, u);
 76 //            edge[u].push_back(v);
 77 //            edge[v].push_back(u);
 78         }
 79         cnt = 0;
 80         dfs(p, p);
 81 
 82         for(int i = 1; i <= cnt; i++) {
 83             if( st[que[i]] == 0 ) st[que[i]] = i;
 84             else se[que[i]] = i;
 85         }
 86 
 87         for(int i = 1; i <= cnt; i++) {
 88 //            printf("%d  ", que[i]);
 89             Update(i, 1);
 90         }
 91         putchar('\n');
 92 //因为题意中要求子树中的节点不能比根节点的数大,因此从后往前枚举,
 93 //然后删除掉枚举过的节点,这样就能保证后面的枚举中子树的点的数不会比根节点的大了
 94 //而且题目的询问是不包含该询问的节点的,因此是(Query(se[i] - 1) - Query(st[i])) / 2
 95 //而不是( Query(se[i]) - (Query[st[i]] - 1) ) / 2
 96         for(int i = n; i > 0; i--) {
 97             ans[i] = (Query(se[i] - 1) - Query(st[i])) / 2;
 98             Update(se[i], -1);
 99             Update(st[i], -1);
100         }
101 
102         for(int i = 1; i <= n; i++) {
103             printf("%d", ans[i]);
104             if( i == n ) printf("\n");
105             else printf(" ");
106         }
107     }
108     return 0;
109 }
View Code

我根据上题的代码也写了一份POJ 3321的代码,顺利AC了之后,看了看别人的代码,发现自己处理的有点复杂 (就是MDZZ)。
先上一份看了别人之后修改过的DFS的代码
1 void dfs(int u, int fa)
2 {
3     st[u] = ++cnt;
4     for(int k = head[u]; ~k; k = edge[k].nxt){
5         int v = edge[k].v;
6         if( v != fa ) dfs(v, u);
7     }
8     ed[u] = cnt;
9 }
其实完全可以抛弃上一题的que数组,直接用这样的方式来处理st和ed数组。
虽然我觉得用que数组的话真的很好理解,但是变得比较冗杂了。
我们还是举HDU 3887的例子(谁让POJ 3321这个样例太不好举例了。。)
我们这样处理的话就是直接去掉我上面的重复的数字,变成:
node:7 1 3 15 12 4 10 14 2 13 9 11 6 5 8  
st和ed两个数组对应的下标分别是:
st: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
ed:15 2 5 4 5 6 10 10 9 10 15 12 15 14 15 (对不齐,将就看看吧= =)
我们直接根据这样就可以建立一个树状数组了。
例如节点7的领域:【1,15】,
  节点1的领域:【2,2】,
  以此类推。
然后因为我们的树状数组变了,所以我们的询问和更新也肯定变了。
询问还是  【 st[node] - 1 ,ed[node] 】,只不过区间的值变小了,因为没有重复,我们不用对答案除以2了。
更新的区间就只要更新st[node]了,因为看上面,st[node]代表的是当下标取node时对应于该节点的左端点,所以只要更新st[node]就可以了。
还有这题可能卡了vector,我用的是前向星,一开始用vector超时了。
下面给出代码
  1 #include <cstdio>
  2 #include <cstring>
  3 #include <iostream>
  4 #include <algorithm>
  5 using namespace std;
  6 #define N 100005
  7 struct node
  8 {
  9     int v, nxt;
 10 }edge[N*2];
 11 int bit[N], st[N], ed[N], head[N], mark[N], cnt, tot, n;
 12 
 13 void add(int u,int v)
 14 {
 15     edge[tot].v = v;
 16     edge[tot].nxt = head[u];
 17     head[u] = tot++;
 18     edge[tot].v = u;
 19     edge[tot].nxt = head[v];
 20     head[v] = tot++;
 21 }
 22 
 23 int lowbit(int x)
 24 {
 25     return x & (-x);
 26 }
 27 
 28 int Query(int x)
 29 {
 30     int ans = 0;
 31     while( x > 0 ){
 32         ans += bit[x];
 33         x -= lowbit(x);
 34     }
 35     return ans;
 36 }
 37 
 38 void Update(int x, int value)
 39 {
 40     while( x <= n ){
 41         bit[x] += value;
 42         x += lowbit(x);
 43     }
 44 }
 45 
 46 void dfs(int u, int fa)
 47 {
 48     st[u] = ++cnt;
 49     for(int k = head[u]; ~k; k = edge[k].nxt){
 50         int v = edge[k].v;
 51         if( v != fa ) dfs(v, u);
 52     }
 53     ed[u] = cnt;
 54 }
 55 
 56 int main()
 57 {
 58     tot = 0, cnt = 0;
 59     memset(bit, 0, sizeof(bit));
 60     memset(head, -1, sizeof(head));
 61     scanf("%d", &n);
 62     for(int i = 1; i < n; i++){
 63         int u, v;
 64         scanf("%d%d", &u, &v);
 65         add(u, v);
 66     }
 67     for(int i = 1; i <= n; i++){
 68         Update(i, 1);
 69         mark[i] = 1;
 70     }
 71 
 72     dfs(1, -1);
 73 
 74     int q;
 75     scanf("%d", &q);
 76     while(q--){
 77         char s[3];
 78         int node;
 79         scanf("%s%d", s, &node);
 80         if(s[0] == 'Q'){
 81             int ans = Query(ed[node]) - Query(st[node] - 1);
 82             printf("%d\n", ans);
 83         }
 84         else{
 85             mark[node] *= -1;
 86             Update(st[node], mark[node]);
 87         }
 88         printf("%d  %d\n", bit[st[1]], bit[ed[1]]);
 89     }
 90     return 0;
 91 }
 92 //15
 93 //1 10
 94 //1 7
 95 //1 9
 96 //1 3
 97 //1 4
 98 //10 14
 99 //14 2
100 //14 13
101 //9 11
102 //9 6
103 //6 5
104 //6 8
105 //3 15
106 //3 12
107 //8
108 //Q 1
109 //C 1
110 //C 3
111 //C 5
112 //Q 1
113 //C 1
114 //Q 1
115 //Q 5
 
posted @ 2016-07-18 20:28  Shadowdsp  阅读(227)  评论(0编辑  收藏  举报