P7126 [Ynoi2008] rdCcot

题目描述

给定一棵大小为n的树,两个点x,y直接连通当且仅当dis(x,y)CC为一个给定常数
q次询问,每次询问问你,如果只保留编号在[l,r]中的点,会有几个连通块

题解

dis(x,y)=1的一般做法是算点数和边数,但无法扩展
考虑对于每个连通块找一个代表元来计数
首先第一个想法,对于每个点i,求出LiRi表示i左边和右边跟i编号差距最小的点,并且满足depLidepi,depRidepi
这样每个连通块就只有最浅的点会作为代表元
进一步地,条件改为depRi<depi,这样就只有最浅且编号最小的点会作为代表元
LiRi可以点分治后,按dep排序,编号为键值插入线段树,维护dis表示距离重心的距离的最小值,然后在线段树上二分
求出LiRi之后问题就很简单了,扫描线+树状数组即可
时间复杂度O(nlog2n)
比较卡常

code

#include<cstdio>
#include<algorithm>
#include<vector>
using namespace std;
char buf[1 << 21], *p1 = buf, *p2 = buf;
int getc() {
	return p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 1 << 21, stdin), p1 == p2) ? EOF : *p1++;
}
void read(int &ret)
{
	ret = 0;
	char ch = getc();
	while (ch<'0'||ch>'9') ch = getc();
	while ('0'<=ch&&ch<='9') {
		ret = ret * 10 + ch - 48;
		ch = getc();
	}
}
const int N=3e5+100,M=6e5+100;
int n,m,C,fa[N+10],dep[N+10];
int st[N+10],tot,L[N+10],R[N+10];
struct edge
{
	int to,last;
}e[N<<1|1];
void add(int a,int b)
{
	e[++tot].to=b;
	e[tot].last=st[a];
	st[a]=tot;
}
int sum,siz[N+10],dis[N+10],rt;
bool vis[N+10];
void getsiz(int u,int fa)
{
	siz[u]=1;
	for(int i=st[u],v;i!=0;i=e[i].last)
	{
		v=e[i].to;
		if(v==fa||vis[v]) continue;
		getsiz(v,u),siz[u]+=siz[v];
	}
}
void getrt(int u,int fa)
{
	int maxn=0;
	for(int i=st[u],v;i!=0;i=e[i].last)
	{
		v=e[i].to;
		if(v==fa||vis[v]) continue;
		getrt(v,u),maxn=max(maxn,siz[v]);
		if(rt) return;
	}
	maxn=max(maxn,sum-siz[u]);
	if((maxn<<1)<=sum) rt=u;
}
void dfs(int *dis,int u,int fa)
{
	dis[u]=dis[fa]+1;
	for(int i=st[u],v;i!=0;i=e[i].last)
	{
		v=e[i].to;
		if(v==fa||vis[v]) continue;
		dfs(dis,v,u);
	}
}
int p[N+10];
bool cmp(int a,int b){return dep[a]<dep[b]||(dep[a]==dep[b]&&a<b);}
void dfs1(int *dis,int u,int fa)
{
	dis[u]=dis[fa]+1;
	if(dis[u]<=C) p[++p[0]]=u;
	else return;
	for(int i=st[u],v;i!=0;i=e[i].last)
	{
		v=e[i].to;
		if(v==fa||vis[v]) continue;
		dfs1(dis,v,u);
	}
}
int id[N<<2|1],rid[N+10];
#define ls (p<<1)
#define rs (p<<1|1)
#define mid (l+r>>1)
void build(int p=1,int l=1,int r=n)
{
	if(l==r)
	{
		rid[l]=p;
		return;
	}
	build(ls,l,mid),build(rs,mid+1,r);
}
void update(int x,int p=1,int l=1,int r=n)
{
	if(!id[p]) id[p]=x;
	else if(dis[x]<dis[id[p]]) id[p]=x;
	if(l==r) return;
	if(x<=mid) update(x,ls,l,mid);
	else update(x,rs,mid+1,r);
}
void clear(int p)
{
	if(!id[p]) return;
	id[p]=0;
	clear(p>>1);
}
int querymax(int L,int R,int lim,int p=1,int l=1,int r=n)
{
	if(L>R) return 0;
	if(!id[p]) return 0;
	if(dis[id[p]]>lim) return 0;
	if(l==r) return l;
	int res=0;
	if(R>mid) res=querymax(L,R,lim,rs,mid+1,r);
	if(res) return res;
	if(L<=mid) res=querymax(L,R,lim,ls,l,mid);
	return res;
}
int querymin(int L,int R,int lim,int p=1,int l=1,int r=n)
{
	if(L>R) return 0;
	if(!id[p]) return 0;
	if(dis[id[p]]>lim) return 0;
	if(l==r) return l;
	int res=0;
	if(L<=mid) res=querymin(L,R,lim,ls,l,mid);
	if(res) return res;
	if(R>mid) res=querymin(L,R,lim,rs,mid+1,r);
	return res;
}
#undef ls
#undef rs
#undef mid
bool flag;
void solve(int u)
{
	getsiz(u,0),sum=siz[u];
	rt=0;getrt(u,0),u=rt;
	p[0]=0;
	dfs1(dis,u,0);
	sort(p+1,p+1+p[0],cmp);
	for(int i=1;i<=p[0];i++)
	{
		if(L[p[i]]!=p[i]-1)
		{
			if(flag||!L[p[i]])
			{
				int tmp=querymax(1,p[i],C-dis[p[i]]);
				if(tmp>L[p[i]]) L[p[i]]=tmp;
			}
		}
		update(p[i]);
	}
	for(int i=1;i<=p[0];i++) clear(rid[p[i]]);
	for(int i=1;i<=p[0];i++)
	{
		int tmp;
		if(i!=1&&dep[p[i]]!=dep[p[i-1]])
		{
			tmp=dep[p[i-1]];
			for(int j=i-1;j>=1;j--)
			{
				if(dep[p[j]]!=tmp) break;
				update(p[j]);
			}
		}
		if(R[p[i]]!=p[i]+1)
		{
			tmp=querymin(p[i],n,C-dis[p[i]]);
			if(tmp&&tmp<R[p[i]]) R[p[i]]=tmp;
		}
	}
	for(int i=1;i<=p[0];i++) clear(rid[p[i]]);
	vis[u]=true;
	for(int i=st[u],v;i!=0;i=e[i].last)
	{
		v=e[i].to;
		if(!vis[v])
			solve(v);
	}
}
struct tree
{
	int t[N+10];
	int lowbit(int x){return x&-x;}
	void update(int x,int k){for(;x<=n;x+=lowbit(x)) t[x]+=k;}
	void update(int l,int r,int k){if(l>r) return;update(l,k),update(r+1,-k);}
	int query(int x)
	{
		int res=0;
		for(;x;x-=lowbit(x)) res+=t[x];
		return res;
	}
}t;
vector<int> g[N+10];
vector<pair<int,int> > qst[N+10];
int ans[M+10];
namespace output
{
	char buf[1 << 21], a[20]; int p, p2 = -1;
	inline void flush() {
		fwrite(buf, 1, p2 + 1, stdout);
		p2 = -1;
	}
	inline void print(int x) {
		if (p2 > 1 << 20) flush();
		if (x < 0) buf[++p2] = 45, x = -x;
		do {
			a[++p] = x % 10 + 48;
		} while (x /= 10);
		do {
			buf[++p2] = a[p];
		} while (--p);
		buf[++p2] = '\n';
	}
}
int main()
{
//	freopen("a.in","r",stdin);
//	freopen("a.out","w",stdout);
	dep[0]=dis[0]=-1;
	read(n),read(m),read(C);
	for(int i=2;i<=n;i++) read(fa[i]),add(i,fa[i]),add(fa[i],i);
	for(int i=1;i<=n;i++) R[i]=n+1;
	dfs(dep,1,0);
	build();
	for(int i=1,l,r;i<=m;i++)
	{
		read(l),read(r),qst[r].push_back({l,i});
		if(l!=1) flag=true;
	}
	solve(1);
	for(int i=1;i<=n;i++) g[R[i]].push_back(i);
	for(int i=1;i<=n;i++)
	{
		t.update(L[i]+1,i,1);
		for(int v:g[i]) t.update(L[v]+1,v,-1);
		for(auto v:qst[i])
			ans[v.second]=t.query(v.first);
	}
	for(int i=1;i<=m;i++) output::print(ans[i]);
	output::flush();
	return 0;
}
posted @   _doctorZ  阅读(6)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
· 【杂谈】分布式事务——高大上的无用知识?
点击右上角即可分享
微信分享提示