西瓜树之二

题目描述

给定一棵 N 个节点的西瓜树,第 i 个点的编号为 i,第j条边的编号为j

Q次查询,每次给出两个整数l,r,查询如果只保留树上点编号在[l,r]内的点,边编号在[l,r]内的边,还剩下多少点连通块。

提示,给出 l,r 后,此时点 ab 连通等价于 la,brab 在树上的简单路径中所有点与边编号都在 [l,r] 之间。

N,Q106

解法

一道树状数组的题,鬼知道这个华强专题的测试为什么那么喜欢考树(话说华强的西瓜为什么会长在树上,以及萨日朗到底是什么梗)。

询问之间互相独立且不带修改,考虑离线。

首先有一个结论就是,假如对于一堆树上的节点,每在它们任意两个之间连一条边,连通块就会减少一个,原因很简单。树的特性决定了任意一条边都只会连接两个不同的连通块(不然就会出现环,那就不是树了,毕竟西瓜树它也是树啊)。

于是做如下考虑:假如只保留点不保留边,那么会得到 rl+1 个连通块;接下来考虑所有编号在[l,r]中的边有多少条连接的两个点都是在 [l,r] 的区间内。

可以给予每条边三个参数 id,x,y ,问题就转化成了有多少条边的三个参数都是在 [l,r] 里的。进而把每条边的参数优化成 max,min,问题就变成了有多少点的两个参数都是在 [l,r] 内的,于是问题就转化成了平面上矩形内点数的问题。

树状数组离线处理即可。

#include<cstdio>
#include<algorithm>
//#define zczc
using namespace std;
const int N=1000010;
inline void read(int &wh){
    wh=0;int f=1;char w=getchar();
    while(w<'0'||w>'9'){if(w=='-')f=-1;w=getchar();}
    while(w<='9'&&w>='0'){wh=wh*10+w-'0';w=getchar(); }
    wh*=f;return;
}
inline int min(int s1,int s2){
	return s1<s2?s1:s2;
}
inline int max(int s1,int s2){
	return s1<s2?s2:s1;
}

int m,n,s[N],an[N];
namespace g{
	struct no{
		int x,y,pl;
		bool change,add;
	}b[N*6];
	inline bool cmp(no s1,no s2){
		if(s1.x==s2.x){
			if(s1.y==s2.y)return s1.change;
			else return s1.y<s2.y;
		}
		else return s1.x<s2.x;
	}
	int cnt;
	#define lowbit (wh&-wh)
	int t[N];
	void change(int wh){
		for(;wh<=m;wh+=lowbit)t[wh]++;
        return;
	}
	int work(int wh){
		int an=0;
		for(;wh;wh-=lowbit)an+=t[wh];
		return an;
	}
	#undef lowbit
	void add(int pl,int s1,int s2,bool change,bool add){
		cnt++;
		b[cnt].pl=pl;
		b[cnt].x=s1;
		b[cnt].y=s2;
		b[cnt].change=change;
		b[cnt].add=add;
		return;
	}
	void solve(){
		sort(b+1,b+cnt+1,cmp);
		for(int i=1;i<=cnt;i++){
			if(b[i].change){
				change(b[i].y);
				continue;
			}
			int now=work(b[i].y);
			an[b[i].pl]+=now*(b[i].add?1:-1);
		}
		return;
	}
}

signed main(){
	
	#ifdef zczc
	freopen("in.txt","r",stdin);
	#endif
	
	int s1,s2;
	read(m);read(n);
	for(int i=1;i<m;i++){
		read(s1);read(s2);
		g::add(0,min(min(s1,s2),i),max(max(s1,s2),i),true,false);
	}
	for(int i=1;i<=n;i++){
		read(s1);read(s2);
		s[i]=s2-s1+1;
		g::add(i,s2,s2,false,true);
		g::add(i,s1-1,s1-1,false,true);
		g::add(i,s1-1,s2,false,false);
		g::add(i,s2,s1-1,false,false);
	}
	g::solve();
	for(int i=1;i<=n;i++)printf("%d\n",s[i]-an[i]);
	
	return 0;
}
posted @   Feyn618  阅读(144)  评论(0编辑  收藏  举报
编辑推荐:
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具
点击右上角即可分享
微信分享提示