描述
幻想乡一共有n处居所,编号从1到n。这些居所被n-1条边连起来,形成了一个树形的结构。
每处居所都居住着一个小精灵。每天小精灵们都会选出一个区间[l,r],居所编号在这个区间内的小精灵一起来完成一项任务。
特别的,居所相邻的(有边相连的)两个小精灵会自发的组成一队,并且如果a和b相邻b和c相邻,那么a和c也在同一队里面。每天的任务完成之后,队伍就会解散;第二天再根据新的区间组成新的队伍。
给出每天小精灵们选出的区间,你知道每天组成的队伍数量吗?
输入
第一行两个数n和Q(1 <= n, Q <= 100000),表示居所的数目和组队的天数。
接下来n-1行,每行两个数a和b,表示居所a和b之间有一条边。
接下来Q行,每行两个数l和r,满足1<=l<=r<=n,为该天小精灵选出的区间。
输出
输出Q行,每行一个整数表示该天队伍的数量。
样例输入
3 1 1 2 2 3 1 3
样例输出
1
草草看完题,妈妈我不会做啊。再看一遍,原来是一颗树!
那么一个区间[l,r]的答案就是r-l+1-满足l<=ui<=r&&l<=vi<=r的边的数目。
那么这就是一个二维偏序模型,离线用个Fenwich什么的就行了。
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
#include<cstdio> #include<cctype> #include<queue> #include<stack> #include<cstring> #include<algorithm> #define rep(i,s,t) for(int i=s;i<=t;i++) #define dwn(i,s,t) for(int i=s;i>=t;i--) #define ren for(int i=first[x];i;i=next[i]) using namespace std; inline int read() { int x=0,f=1;char c=getchar(); for(;!isdigit(c);c=getchar()) if(c=='-') f=-1; for(;isdigit(c);c=getchar()) x=x*10+c-'0'; return x*f; } const int maxn=200010; int n,q,u[maxn],v[maxn],ans[maxn],e[maxn]; struct Query { int x,l,r,id,tp; bool operator < (const Query& ths) const {return x<ths.x;} }Q[maxn]; int cmp(int x,int y) {return u[x]<u[y];} int sumv[maxn]; int sum(int x) { int res=0; for(;x;x-=x&-x) res+=sumv[x]; return res; } void add(int x) {for(;x<=n;x+=x&-x) sumv[x]++;} int main() { n=read();q=read(); rep(i,1,n-1) u[i]=read(),v[i]=read(),e[i]=i; rep(i,1,q) { int l=read(),r=read(); Q[i*2-1]=(Query){r,l,r,i,1}; Q[i*2]=(Query){l-1,l,r,i,-1}; ans[i]=r-l+1; } q<<=1;int cur=0; sort(Q+1,Q+q+1);sort(e+1,e+n,cmp); rep(i,1,q) { while(cur<n-1&&u[e[cur+1]]<=Q[i].x) add(v[e[++cur]]); ans[Q[i].id]-=Q[i].tp*(sum(Q[i].r)-sum(Q[i].l-1)); } rep(i,1,q>>1) printf("%d\n",ans[i]); return 0; }