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掌握的不好。。。。不会构树这是个问题。。。。。。。。。。

 

posted @ 2013-08-02 17:00  SunnySnail  阅读(186)  评论(0编辑  收藏  举报