题意:给一颗树,q个询问,每个询问求节点标号[L,R]之间的节点组成的连通块的个数。
题解:由于是树,可以知道每当加一条连通块的数目就会少一个,所以题目就可以变为,求区间[L,R]之间包含的边的个数,然后我们先将所有边的编号大的点加入树状数组,然后枚举区间左端点,对于当前询问,从树状数组中找出小于等于r的边的个数,就是[L,R]包含的边的个数,注意要删掉右端点在L之前的边。
AC代码:
#include<stdio.h>
#include<vector>
#include<string.h>
#include<iostream>
#include<algorithm>
using namespace std;
vector<int>vt[200005];
struct node
{
int l,r,id;
}q[200005];
bool cmp(node a,node b)
{
return a.l<b.l;
}
int c[200005],ans[200005],n,m;
int lowbit(int i)
{
return i&(-i);
}
void change(int pos,int k)
{
while(pos<=n)
{
c[pos]+=k;
pos+=lowbit(pos);
}
}
int Sum(int pos)
{
int ans=0;
while(pos)
{
ans+=c[pos];
pos-=lowbit(pos);
}
return ans;
}
int main()
{
int T;
scanf("%d",&T);
while(T--)
{
memset(c,0,sizeof(c));
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)vt[i].clear();
for(int i=0;i<n-1;i++)
{
int u,v;
scanf("%d%d",&u,&v);
vt[min(u,v)].push_back(max(u,v));
change(max(u,v),1);
}
for(int i=0;i<m;i++)
scanf("%d%d",&q[i].l,&q[i].r),q[i].id=i;
sort(q,q+m,cmp);
int now=0;
for(int i=1;i<=n;i++)
{
while(now<m&&q[now].l==i)
{
ans[q[now].id]=q[now].r-q[now].l+1-Sum(q[now].r);
now++;
}
for(int j=0;j<vt[i].size();j++)
change(vt[i][j],-1);
}
for(int i=0;i<m;i++)
printf("%d\n",ans[i]);
}
}