飘雪圣域

题目描述

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]内的联通快个数。
对于联通块个数,我们思考一下统计方式

  1. 直接DFS 效率O(n)
  2. 对于树:点数-边数

直接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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Jarden_

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值