题目描述
IcePrincess_1968 和 IcePrince_1968 长大了,他们开始协助国王 IceKing_1968 管理国内事物。
IcePrincess_1968 和 IcePrince_1968 住在一个宁静悠远的王国:IceKingdom —— 飘雪圣域。飘雪圣域有 n 个城镇,编号 1,2,3…n。有些城镇之间有道路,且满足任意两点之间有且仅有一条路径。飘雪圣域风景优美,但气候并不是太好。根据 IcePrince_1968 的气候探测仪,将来会发生 q 场暴风雪。每场暴风雪可以用两个整数 li,ri 刻画,表示这场暴风雪之后,只有编号属于[li,ri]的城市没有受到暴风雪的影响。
在暴风雪的影响下迅速确定王国的农业生产方案是非常重要的事情。IceKing_1968 认为,一个农业生产地域应该是一个极大连通块,满足每个节点都没有被暴风雪影响。这里极大连通块的定义是:不存在一个不属于该点集的未被暴风雪影响的点与该连通块连通。
IcePrincess_1968 要负责算出每次暴风雪后,王国能拥有多少个农业生产地域。注意这里每次暴风雪是独立的,即每次暴风雪过后,直到每个城镇重新焕发生机,下一次暴风雪才会到来。
正如上文所述,IcePrincess_1968 擅长文学但不擅长计算机,于是请你帮忙。
题解
观察到我们每次找的是[l,r]内的联通快个数。
对于联通块个数,我们思考一下统计方式
- 直接DFS 效率O(n)
- 对于树:点数-边数
直接DFS太暴力了根本不知道怎么写,如果盯着图论看一辈子也想不出来怎么写。
那么我们转换一下思路,求这个区间内的边数,那我们就惊喜地发现这是一个二维偏序问题,树状数组暴搞即可。
具体地:
我们对询问区间和边都按一维排序,保证维护使得在树状数组里的边的那一维都是在当前查询区间的里面的,另一维直接用树状数组即可
后记
事实上这道题的特殊性在于询问树的连通性而不是图的,那么它就应该用到树的性质,树的最大性质就是边,所以应该从边下手去思考问题
代码
#include<bits/stdc++.h>
#define maxn 200005
#define MAXN 1000005
#define INF 0x3f3f3f3f
#define LL long long
using namespace std;
LL read(){
LL res,f=1; char c;
while(!isdigit(c=getchar())) if(c=='-') f=-1; res=(c^48);
while(isdigit(c=getchar())) res=(res<<3)+(res<<1)+(c^48);
return res*f;
}
int n,m;
int arr[maxn];
struct EDGE{
int x,y,id;
bool operator < (const EDGE &rhs)const{
return y>rhs.y;
}
}e[maxn],q[maxn];
void update(int x,int w){for(;x;x-=x&-x) arr[x]+=w;}
int query(int x){int res=0; for(;x<=n;x+=x&-x) res+=arr[x]; return res;}
int ans[maxn];
int main(){
n=read(); m=read();
for(int i=1;i<n;i++){
e[i].x=read();
e[i].y=read();
if(e[i].x>e[i].y) swap(e[i].x,e[i].y);
update(e[i].x,1);
}
for(int i=1;i<=m;i++){
q[i].x=read();
q[i].y=read();
q[i].id=i;
}
sort(e+1,e+n);
sort(q+1,q+m+1);
for(int i=1,j=1;j<=m;j++){
while(e[i].y>q[j].y && i<=n-1){
update(e[i].x,-1);
i++;
}
ans[q[j].id]=(q[j].y-q[j].x+1)-query(q[j].x);
}
for(int i=1;i<=m;i++){printf("%d\n",ans[i]);}
return 0;
}