poj 3321 Apple Tree(分块大法)

题目大意:

给你一颗树,最初每个节点上都有一个苹果,有两种操作:
1,修改(即修改某一个节点,修改时这一个节点苹果从有到无,或从无到有。
2,查询(查询某一个节点他的子树上有多少个苹果)。
解决这个区间问题,首先想到线段树(我不会),树状数组(我不会),哎呀!像我这样的蒟蒻当然有蒟蒻的方法
分块---优雅的暴力!

思路:


1,首先我们要把这个树转化为一个一维数组,这样我们就可以对它进行区间修改和查询。转化的过程就是DFS序(https://www.cnblogs.com/prjruckyone/p/12754936.html
2,然后进行分块(分块一点都不会的先学习一下:https://blog.csdn.net/weixin_46560570/article/details/105114268
3,分块后就可以开始我们的蜜汁操作了。

变量名解释

start[maxn]:当给你一个分岔时他的起点。
end[maxn]:当给你一个分岔时他的终点。
例如
                    1
                  /   \
                2     3
             /     \
           4      5
当给你一个分岔2时  其起点就是2  终点就是5 这个分岔包含 2 4 5;
belong[maxn]:一个块;
sum[maxn]:一个块内苹果的数目;
R[maxn]:一个块内的右端;
L[maxn];一个块内的左端;
apple[maxn]:分岔上的苹果,1为有苹果,0表示没有。
在DFS中这一步   :  sort(Q[x].begin(),Q[x].end());

例如上图样例:

5
1 2
1 3
2 5
2 4
咦! 你会发现如果按次顺序存放

先存放2 5 再存放 2 4
Q[a].push_back(b);
上图中 4和5的位置是翻着的因此我们进行一个sort排序。

//下面进入正题:
#include <iostream>
#include <cmath>
#include <algorithm>
#include <cstdio>
#include <vector>
using namespace std;
const int maxn = 1e6+1000;
vector<vector<int> > Q(maxn);//容器。
int start[maxn],end[maxn],belong[maxn],sum[maxn],R[maxn],L[maxn],apple[maxn];
int n,time,block,m,num;   //n:总数目,time:用于计数   block:块的大小     m:操作数目    num:块的数量;
void dfs(int x){              //DFS序(https://www.cnblogs.com/prjruckyone/p/12754936.html)
    time++;
    start[x]=time;//记录开始
    sort(Q[x].begin(),Q[x].end());
    for(int i=0;i<Q[x].size();i++)
        dfs(Q[x][i]);
    end[x]=time;//记录结尾
}
void build()
{
    block=sqrt(n);//分块
    num=n/block;
    if(n%block) num++;//如果没有除尽 加一块,没除尽说明有余数,不能不要呀,所以加一块。
    for(int i=1;i<=num;i++){
        L[i]=(i-1)*block+1;   // 每一块的左端点
        R[i]=i*block;  // 每一块的右端点
    }
    R[num]=n;  //最后一个肯定是最右边的端点
    for(int i=1;i<=n;i++)
        belong[i]=(i-1)/block+1;  //每一个点所在的块
    for(int i=1;i<=num;i++)
        for(int j=L[i];j<=R[i];j++)
            ++sum[i];//刚开始都是有苹果的 ,在这里计算每一块中的苹果数目;
}
void update(int x){//更改
    if(apple[x]){//有苹果 摘下来
        sum[belong[x]]--; //该分岔所在的块的总苹果数目减一
        apple[x]=0; //1为有苹果,0表示没有。
    }
    else{
        apple[x]=1;
        sum[belong[x]]++;//该分岔所在的块的总苹果数目加一
    }
}
int query(int x,int y){//查询。
    int ans=0;
    if(belong[x]==belong[y]){//如果在一个块内就遍历这一个块
        for(int i=x;i<=y;i++)
            if(apple[i])  ++ans;
        return ans; 
    }
    else{
        for(int i=x;i<=R[belong[x]];i++)//遍历x到x所在块的右端
            if(apple[i]) ++ans;
        for(int i=L[belong[y]];i<=y;i++)//遍历y所在块的左端到y点;
            if(apple[i]) ++ans;
        for(int i=belong[x]+1;i<belong[y];i++)//按块遍历;
             ans=ans+sum[i];
        return ans;
    }
}
/*20
1 2
1 3
1 4
2 5
2 7
2 8
2 9
5 13
5 14
5 15
7 16
7 17
9 18
3 6
3 10
4 11
4 12
11 19
12 20

在该数据下DFS序列(树的前序遍历)为:
    1 2 5 13 14 15 7 16 17 8 9 18 3 6 10 4 11 19 12 20
当你输入 C 3时 你修改的正是苹果树上的第5分岔   第5分岔在DFS序列的3号位置。(细品,实在想不明白自己把这个树画出来)
此时statr数组为:1 2 13 16 3 14 7 10 11 15 17 19 4 5 6 8 9 12 18 20
此时end数组为:20 12 15 20 6 14 9 10 12 15 18 20 4 5 6 8 9 12 18 20
哎呀,你看巧妙不巧妙  分岔1包含的苹果就是DFS序列1-20的和,分岔2包含的苹果就是DFS序列2-12的和。
*/
int main()
{
    scanf("%d",&n);
    for(int i=1;i<n;i++){
        int a,b;
        scanf("%d%d",&a,&b);
        Q[a].push_back(b);
        apple[i]=1;//开始都有苹果。
    }
    apple[n]=1;
    dfs(1);
    build();
    scanf("%d",&m);
    for(int i=1;i<=m;i++)
    {
        char ch;
        int k;
        scanf(" %c",&ch);
        scanf("%d",&k);
        if(ch=='C') update(start[k]);//当你输入你个分岔位置,statr[k] 所对应的点就是要修改的点位
        else if(ch=='Q') 
            printf("%d\n",query(start[k],end[k]));//看上面分析,细细品味。
    }
    return 0;
}

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

@李思成

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值