BZOJ 七彩树
written on 2022-05-02
问题我想留在开头:为什么主席树的空间开45倍都过不了,要开50?
题目最大的困难在于有深度限制,而且强制在线,否则就是裸的线段树合并了
既然题目大胆地给了深度限制,那么这题大胆的正解之一就是把点按照深度从小到大排序,用主席树来维护区间和。
哇塞好恐怖!
在循环的时候,还要考虑重复的点对答案的影响,这个时候,发现两个颜色相同的点重复的部分,就在于他们的lca(因为主席树是以前缀和的形式维护的)。
于是从浅到深枚举,每次加入一个颜色的点,还要消除与它相邻的两个同颜色点的影响,这个可以用set维护dfs序。(注意:这个点相邻的两个点lca要加一,因为上一次减了1,这次要消回来)
查询时为了方便,
写这篇题解好累,不懂的就看代码吧,写得还算看的过去
#include<bits/stdc++.h>
#define N 100005
using namespace std;
int n,m;
int tot,ver[N],nxt[N],head[N];
void add_E(int x,int y){ver[++tot]=y,nxt[tot]=head[x],head[x]=tot;}
int c[N],dep[N];
struct F{int id,col,dep;}b[N];
bool cmp(F a,F b){return a.dep<b.dep;}
int f[N][20];
int lca(int x,int y)
{
if(dep[x]<dep[y]) swap(x,y);
for(int i=19;i>=0;i--) if(dep[f[x][i]]>=dep[y]) x=f[x][i];
if(x==y) return x;
for(int i=19;i>=0;i--) if(f[x][i]!=f[y][i]) x=f[x][i],y=f[y][i];
return f[x][0];
}
int dfn[N],rnk[N],siz[N],rt[N],mx;
void dfs(int x)
{
for(int i=1;i<=19;i++) f[x][i]=f[f[x][i-1]][i-1];
dfn[x]=++tot,rnk[tot]=x;
b[x].dep=dep[x],siz[x]=1;
mx=max(mx,dep[x]);
for(int i=head[x];i;i=nxt[i])
{
int y=ver[i];
dep[y]=dep[x]+1;
dfs(y);
siz[x]+=siz[y];
}
}
struct Seg
{
int tot,ls[N*55],rs[N*55],val[N*55];//所以为什么空间要开到55?
int build(int l,int r)
{
int p=++tot;
ls[p]=rs[p]=val[p]=0;
if(l==r) return p;
int mid=l+r>>1;
ls[p]=build(l,mid),rs[p]=build(mid+1,r);
return p;
}
int New(int lst,int l,int r,int x,int v)
{
int p=++tot;
ls[p]=ls[lst],rs[p]=rs[lst],val[p]=val[lst];
if(l==r)
{
val[p]+=v;
return p;
}
int mid=l+r>>1;
if(x<=mid) ls[p]=New(ls[p],l,mid,x,v);
else rs[p]=New(rs[p],mid+1,r,x,v);
val[p]=val[ls[p]]+val[rs[p]];
return p;
}
int ask(int p,int l,int r,int L,int R)
{
if(L<=l&&R>=r) return val[p];
int mid=l+r>>1,res=0;
if(L<=mid) res+=ask(ls[p],l,mid,L,R);
if(R>mid) res+=ask(rs[p],mid+1,r,L,R);
return res;
}
}t1;
set<int> s[N];
int main()
{
int T;
scanf("%d",&T);
while(T--)
{
mx=tot=0,memset(head,0,sizeof(head));
t1.tot=0;
memset(rt,0,sizeof(rt));
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
{
scanf("%d",&c[i]);
b[i].col=c[i],b[i].id=i;
s[i].clear();
}
for(int i=2;i<=n;i++)
{
scanf("%d",&f[i][0]);
add_E(f[i][0],i);
}
tot=0,dep[1]=1,dfs(1);
rt[0]=t1.build(1,n);
sort(b+1,b+1+n,cmp);
for(int i=1;i<=n;i++)
{
if(b[i].dep==b[i-1].dep) rt[b[i].dep]=t1.New(rt[b[i].dep],1,n,dfn[b[i].id],1);
else rt[b[i].dep]=t1.New(rt[b[i-1].dep],1,n,dfn[b[i].id],1);
set<int>::iterator it=s[b[i].col].lower_bound(dfn[b[i].id]);
t1.New(rt[b[i].dep],1,n,dfn[b[i].id],1);
int x=0,y=0;
if(it!=s[b[i].col].end())
{
x=*it;
int l=lca(rnk[x],b[i].id);
rt[b[i].dep]=t1.New(rt[b[i].dep],1,n,dfn[l],-1);
}
if(it!=s[b[i].col].begin())
{
y=*(--it);
int l=lca(rnk[y],b[i].id);
rt[b[i].dep]=t1.New(rt[b[i].dep],1,n,dfn[l],-1);
}
if(x&&y)
{
int l=lca(rnk[x],rnk[y]);
rt[b[i].dep]=t1.New(rt[b[i].dep],1,n,dfn[l],1);
}
s[b[i].col].insert(dfn[b[i].id]);
}
int ans=0;
for(int i=1;i<=m;i++)
{
int x,y;
scanf("%d%d",&x,&y);
x=x^ans,y=y^ans;
printf("%d\n",ans=t1.ask(rt[min(mx,dep[x]+y)],1,n,dfn[x],dfn[x]+siz[x]-1));
}
}
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!