P7126 [Ynoi2008] rdCcot
题目描述
给定一棵大小为的树,两个点直接连通当且仅当,为一个给定常数
次询问,每次询问问你,如果只保留编号在中的点,会有几个连通块
题解
的一般做法是算点数和边数,但无法扩展
考虑对于每个连通块找一个代表元来计数
首先第一个想法,对于每个点,求出和表示左边和右边跟编号差距最小的点,并且满足
这样每个连通块就只有最浅的点会作为代表元
进一步地,条件改为,这样就只有最浅且编号最小的点会作为代表元
求和可以点分治后,按排序,编号为键值插入线段树,维护表示距离重心的距离的最小值,然后在线段树上二分
求出和之后问题就很简单了,扫描线+树状数组即可
时间复杂度
比较卡常
#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;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
· 【杂谈】分布式事务——高大上的无用知识?