题意:给一棵树,多次询问编号在l到r中的点组成的连通块个数
考试的时候一直在想奇怪的做法,用的都是树上维护的知识(比如已经被某C姓神仙卡掉的虚树+树剖+二分)
其实这道题根本不需要树上的知识
首先要知道一个区间的连通块个数等于点数-边数(无重边自环的情况)
然后问题就变成了要求端点一个区间内的边数了,这是一个二维偏序问题
所以先对一维进行排序,另一维用树状数组查询就A了
Code:
#include<bits/stdc++.h>
using namespace std;
inline int read(){
int res=0,f=1;char ch=getchar();
while(!isdigit(ch)) {if(ch=='-') f=-f;ch=getchar();}
while(isdigit(ch)) {res=(res<<1)+(res<<3)+(ch^48);ch=getchar();}
return res*f;
}
const int N=200005;
int n,q,tr[N],ans[N];
struct seq{int l,r,id;}e[N],s[N];
inline bool cmp(seq x,seq y){return x.r<y.r;}
inline void add(int k,int v){for(int i=k;i<=n;i+=i&-i) tr[i]+=v;}
inline int ask(int k){
int res=0;
for(int i=k;i;i-=i&-i) res+=tr[i];
return res;
}
int main(){
n=read(),q=read();
for(int i=1;i<n;i++){
e[i].l=read(),e[i].r=read();
if(e[i].l>e[i].r) swap(e[i].l,e[i].r);
}
sort(e+1,e+n,cmp);
for(int i=1;i<=q;i++) s[i].l=read(),s[i].r=read(),s[i].id=i;
sort(s+1,s+q+1,cmp);
int now=1;
for(int i=1;i<=q;i++){
while(s[i].r>=e[now].r&&now<n) add(e[now++].l,1);
int num=ask(s[i].r)-ask(s[i].l-1);
ans[s[i].id]=s[i].r-s[i].l+1-num;
}
for(int i=1;i<=q;i++) cout<<ans[i]<<"\n";
return 0;
}