[树状数组]飘雪圣域

没找到传送门(这是校内的)

题意:给一棵树,多次询问编号在l到r中的点组成的连通块个数

考试的时候一直在想奇怪的做法,用的都是树上维护的知识(比如已经被某C姓神仙卡掉的虚树+树剖+二分)
其实这道题根本不需要树上的知识
首先要知道一个区间的连通块个数等于点数-边数(无重边自环的情况)
然后问题就变成了要求端点一个区间内的边数了,这是一个二维偏序问题
所以先对一维进行排序,另一维用树状数组查询就A了

Code:

#include
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;
}
 

你可能感兴趣的:([树状数组]飘雪圣域)