HYSBZ 2743 (树状数组) 采花

时间:2023-03-09 21:52:29
HYSBZ 2743 (树状数组) 采花

题目:这里

题意:

在2016年,佳媛姐姐刚刚学习了树,非常开心。现在他想解决这样一个问题:给定一颗有根树(根为1),有以下
两种操作:1. 标记操作:对某个结点打上标记(在最开始,只有结点1有标记,其他结点均无标记,而且对于某个
结点,可以打多次标记。)2. 询问操作:询问某个结点最近的一个打了标记的祖先(这个结点本身也算自己的祖
先)你能帮帮他吗?

Input

输入第一行两个正整数N和Q分别表示节点个数和操作次数接下来N-1行,每行两个正整数u,v(1≤u,v≤n)表示u到v
有一条有向边接下来Q行,形如“opernum”oper为“C”时表示这是一个标记操作,oper为“Q”时表示这是一个询
问操作对于每次询问操作,1 ≤ N, Q ≤ 100000。

Output

输出一个正整数,表示结果

Sample Input

5 5
1 2
1 3
2 4
2 5
Q 2
C 2
Q 2
Q 5
Q 3

Sample Output

1
2
2
1

HINT

明显与两个相同的数在数组中的位置有关系。

求出颜色数组的前缀(前面一个该颜色值的位置)或者后缀(后一个该颜色的位置,以后缀为例),这里可以用树状数组解决,将给没个询问区间按照左范围从小到大

排序,然后遍历一边大区间1到n,当 i 小于询问范围左端点的的时候将其加入树状数组,等于询问范围左端点的时候既求区间范围和,注意的是,由于只有同种

颜色的花的数目大于1才算,所以事先将符合条件的花先加进数组,然后遍历的时候考虑失去 i 这个位置的时候是加一还是减一

 #include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std; const int M = 1e6 + ;
int has[M],has1[M],vis[M],b[M],ans[M],n;
int q[M]; struct node{
int x,y,id;
}a[M]; int lowbit(int x){return x&(-x);} void add(int x,int y)
{
while (x<=M){
has[x]+=y;
x+=lowbit(x);
}
} int getsum(int x)
{
int ans=;
while (x>){
ans+=has[x];
x-=lowbit(x);
}
return ans;
} bool cmp(node a,node b)
{
if (a.x==b.x) return a.y<b.y;
return a.x<b.x;
} int main()
{
int c,m;
bool flag=false;
scanf("%d%d%d",&n,&c,&m);
memset(vis,,sizeof(vis));
memset(has1,,sizeof(has1));
for (int i= ; i<=n ; i++){
scanf("%d",&b[i]);
/*if (has1[b[i]]==0) vis[i]=i,has1[b[i]]=i;
else{
vis[i]=has1[b[i]];
has1[b[i]]=i;
}*/
}
for(int i=n ;i>= ; i--){ //求后缀
vis[i]=has1[b[i]];
has1[b[i]]=i;
}
for (int i= ; i<=m ; i++){
scanf("%d%d",&a[i].x,&a[i].y);
a[i].id=i;
}
for(int i= ; i<=c ; i++){ //满足的先加上
if (vis[has1[i]])
add(vis[has1[i]],);
//cout<<has1[i]<<endl;
}
sort(a+,a+m+,cmp);
int j=;
// memset(q,0,sizeof(q));
for(int i= ; i<=m ; i++){
while (j<a[i].x){
if (vis[vis[j]])add(vis[vis[j]],);
if (vis[j]) add(vis[j],-);
j++;
}
ans[a[i].id]=getsum(a[i].y)-getsum(a[i].x-);
}
for (int i= ; i<=m ; i++){
printf("%d\n",ans[i]);
}
//cout<<getsum(1)<<endl;
return ;
}