zoukankan      html  css  js  c++  java
  • POJ--3321(dfs序树状转线性+树状数组+vector防超时写法)

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

    题意:

    树上有n个位置,初始每个位置都有苹果。以1号为总根

    n-1条分叉边

    m条询问:

    Q:id   以id为根的子树有几个苹果

    C:id    id处如果有苹果,摘掉,否则放上一个苹果。

    解析:

    假设输入的是这么一棵树:

    看数据,肯定要前缀和的思想,但是:

    假设要询问4号为根的子树,我们要看的是4+8+9,这些序号很难通过前缀和来维护,因为输入的边并不是按顺序输的。

    所以,考虑能不能把这些序号重新编排一下,使其能通过前缀和来求?

    这里就引入了dfs序

    st[],en[],来记录 i 在dfs序里的开始和结束位置

    比如以此图为例:

    dfs的遍历顺序:1     2   5    6   3     7   4     8    9

    (st[i],en[i]):  (1,9)  (2,4)   (3,3)  (4,4)  (5,6)   (6,6)  (7,9)   (8,8)  (9,9)

    很好理解,结合树,比如4:(7,9)它在dfs序中以第7个开始,第9个结束。

    所以有:

    void dfs(int n)
    {
        num++;
        st[n]=num;
        for(int i=0;i<vv[n].size();i++)
        {
            dfs(vv[n][i]);
        }
        en[n]=num;
    }

    剩下的就交给树状数组维护前缀和了,查询一个点,只需要:getsum(en[id])-getsum(st[id]-1)

    vector防超时的写法:vector<vector<int> > vv(maxn);

    总的代码:

    #include <cstdio>
    #include<cstring>
    #include<map>
    #include<vector>
    #include<iostream>
    using namespace std;
    const int maxn =1e5+10;
    int st[maxn],en[maxn],vis[maxn],c[maxn];
    vector<vector<int> > vv(maxn);
    int num=0;
    int lowbit(int x)
    {
        return x&(-x);
    }
    void update(int id,int x)
    {
        for(int i=id;i<maxn;i+=lowbit(i))
        {
            c[i]+=x;
        }
        return ;
    }
    int getsum(int id)
    {
        int sum=0;
        for(int i=id;i>0;i-=lowbit(i))
            sum+=c[i];
        return sum;
    }
    void dfs(int n)
    {
        num++;
        st[n]=num;
        for(int i=0;i<vv[n].size();i++)
        {
            dfs(vv[n][i]);
        }
        en[n]=num;
    }
    int main()
    {
        int n;
        scanf("%d",&n);
        for(int i=1;i<=n-1;i++)
        {
            int a,b;
            scanf("%d%d",&a,&b);
            vv[a].push_back(b);
        }
        
        dfs(1);
        for(int i=1;i<=n;i++)
        {
            update(i,1);
            vis[i]=1;
        }
        int m;
        scanf("%d",&m);
        while(m--)
        {    
            char ch[12];
            scanf("%s",ch);
            if(ch[0]=='Q')
            {
                int id;
                scanf("%d",&id);        
                cout<<getsum(en[id])-getsum(st[id]-1)<<endl;
            }
            else
            {
                int id;
                scanf("%d",&id);
                if(vis[id])
                {
                    vis[id]=0;
                    update(st[id],-1);
                }
                else
                {
                    vis[id]=1;
                    update(st[id],1);
                }
            }
        }
    }

    参考自:https://www.cnblogs.com/FrankChen831X/p/10800106.html  十分感谢!

  • 相关阅读:
    台阶问题,100层台阶,1,2,3步组合走完。一种有几种组合?
    idea 导入gitlab项目
    查找学生信息
    谁是你潜在的朋友
    Sort
    统计同成绩学生人数
    打印日期
    今年的第几天?
    DayOfWeek
    日期差值
  • 原文地址:https://www.cnblogs.com/liyexin/p/13074188.html
Copyright © 2011-2022 走看看