poj3321-Apple Tree&hoj2743-Apple Tree
地址:http://poj.org/problem?id=3321 http://acm.hit.edu.cn/hoj/problem/view?id=2743
这两道题其实一个意思,题意是一棵苹果树,
分叉,用poj的题意来讲,每个叉有个编号,根节点永远是1,然后按它输入的每两个数相连,构成一棵树,初始每个节点一个苹果,输入C和一个数字,如果此节点有苹果,让它变为0,如果没有苹果,让它变为1,输入Q,查询这个节点的子树上有多少个苹果。
这道题是今天做的第一道DFS序,其实本质还是线段树,单点变换,区间求和,模板都固定,就是看怎么构树,用vector存数据,如1节点连着2,3,那么v[1]里存入2,3,用push_back,然后存完树用DFS(1),排出序,每个节点记录一个开始状态s和末尾状态e,e-s就是它所代表的区间,s是这个节点在线段树中的位置。这些工作做好了以后,直接套入线段树update和query就好了。。。。。。。(查了一下树状数组和线段树的区别,两者复杂度相同,线段树功能更多更广,而树状数组代码简单效率高容易实现,于是我选择了线段树,然后。。。。。这道题很好地实现了。。。。。。。下文还有要吐槽的)
这是poj的题的代码:
#include <iostream> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <math.h> #include <vector> #include <queue> #include <algorithm> using namespace std; const int MAX = 100005; typedef vector<int>q; vector<q>v(MAX); bool vis[MAX],pick[MAX]; int c[MAX],s[MAX],e[MAX],cnt=0,n; inline int lowbit(int i) { return i&(-i); } void Modify(int i,int m)//单点修改 { while ( i <= n ) { c[i]+=m; i += lowbit(i); } } int Sum(int i)//区间求和 { int ret = 0; while ( i > 0 ) { ret += c[i]; i -= lowbit(i); } return ret; } void dfs(int dep) //记录苹果树每个点代表的区间s,e { s[dep] = ++cnt; vis[dep] = 1; for (int i = 0; i <(int) v[dep].size(); i++) { int u = v[dep][i]; if (!vis[u]) dfs(u); } e[dep] = cnt; return; } int main() { while(scanf("%d",&n)==1)//m初始值 { memset(vis, 0, sizeof(vis)); //memset(c, 0, sizeof(c)); for (int i = 0; i <= n; i++) v[i].clear(); for(int i = 1; i < n; i ++) { int a,b; scanf("%d%d", &a, &b); v[a].push_back(b); v[b].push_back(a); } for(int i = 1; i <= n; i ++) Modify(i,1); cnt = 0; dfs(1); int m; scanf("%d",&m); while(m--) { char ch; int x; scanf(" %c %d",&ch,&x); if(ch=='C') { if(pick[x]) { Modify(s[x], 1); pick[x] = false; } else { Modify(s[x], -1); pick[x] = true; } } else { printf("%d\n",Sum(e[x])-Sum(s[x]-1)); } } } return 0; }
好了,本来以为hoj和这个类似,就不写了,但是。。。。。由于题目输出要求不同,发现了树状数组的问题,这道题要求不是0,1替换了,而是给此节点的子树的每个节点都种上一样多的苹果。。。。变成了区间增减。。。。。。。。天啊树状数组不支持成段增减。。。。。。。于是无限后悔刚才为什么没有用线段树做。。。。。又全都改成了线段树(注意求和会很大,不用longlong会WA)下面是hoj的代码
#include <iostream> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <math.h> #include <vector> #include <queue> #include <algorithm> #define lson l , m , rt << 1 #define rson m + 1 , r , rt << 1 | 1 using namespace std; const int MAX = 100005; typedef vector<int>q; vector<q>v(MAX); bool vis[MAX],pick[MAX]; int c[MAX],s[MAX],e[MAX],cnt=0,n; long long sum[MAX<<2]; long long col[MAX<<2]; void PushUP(int rt) { sum[rt] = sum[rt<<1] + sum[rt<<1|1]; } void PushDown(int rt,int m)// 此处与区间替换的差别,区间增减 { if (col[rt]) { col[rt<<1] += col[rt]; col[rt<<1|1] += col[rt]; sum[rt<<1] += (m - (m >> 1)) * col[rt]; sum[rt<<1|1] += (m >> 1) * col[rt]; col[rt] = 0; } } void build(int l,int r,int rt) { col[rt] = 0; if (l == r){ sum[rt]=0; return ; } int m = (l + r) >> 1; build(lson); build(rson); PushUP(rt); } void update(int L,int R,int c,int l,int r,int rt)//区间增减 { if (L <= l && r <= R) { col[rt] += (long long) c; sum[rt] += (long long)c * (r - l + 1); return ; } PushDown(rt , r - l + 1); int m = (l + r) >> 1; if (L <= m) update(L , R , c , lson); if (R > m) update(L , R , c , rson); PushUP(rt); } long long query(int L,int R,int l,int r,int rt)//区间求和 { if (L <= l && r <= R) { return sum[rt]; } PushDown(rt , r - l + 1);/*比单点时多的句子*/ int m = (l + r) >> 1; long long ret = 0; if (L <= m) ret += query(L , R , lson); if (R > m) ret += query(L , R , rson); return ret; } void dfs(int dep) //记录苹果树每个点代表的区间s,e { s[dep] = ++cnt; vis[dep] = 1; for (int i = 0; i <(int) v[dep].size(); i++) { int u = v[dep][i]; if (!vis[u]) dfs(u); } e[dep] = cnt; return; } int main() { int m; while(scanf("%d%d",&n,&m)==2)//m初始值 { memset(vis, 0, sizeof(vis)); for (int i = 0; i <= n; i++) v[i].clear(); build(1,n,1); for(int i = 1; i < n; i ++) { int a,b; scanf("%d%d", &a, &b); v[a].push_back(b); v[b].push_back(a); } cnt = 0; dfs(1); while(m--) { char ch; int x,y; scanf(" %c",&ch); if(ch=='C') { scanf("%d%d",&x,&y); update(s[x],e[x],y,1,n,1); } else { scanf("%d",&x); printf("%lld\n",query(s[x],e[x],1,n,1)); } } } return 0; }
其实这些小问题还好说,主要是DFS掌握的不好。。。。不会构树这是个问题。。。。。。。。。。