题目大意:
给你一颗树,最初每个节点上都有一个苹果,有两种操作:
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;
}