2023NOIP A层联测26 T3 tour
2023NOIP A层联测26 T3 tour
有意思的树上主席树。
思路
首先考虑一个点
记录一个点
第一条,合法条件为
具体来说,每个点继承它父亲的版本,最后把两个版本一减就好了。
第二条,我们记录 lca 的父亲为
左边是一个关于
那么允许离线的方法就很明显了,先建出树,然后对于每个点维护两棵主席树,分别表示第一种和第二种情况,然后查询的时候拆成两条路径分别查询即可(注意不要重复算 lca)。
现在考虑在线。一个经典的方法是启发式合并,由于答案和树的形态无关,可以每次把 size 小的连通块合并到 size 大的连通块上,对于 size 小的那个连通块,我们暴力重构需要维护的信息,包括倍增数组、前缀和数组和主席树。然后正常查询就好了。
启发式合并的复杂度是
CODE
#include<bits/stdc++.h>
using namespace std;
#define lch(p) tree[p].ch[0]
#define rch(p) tree[p].ch[1]
const int maxm=5e7+10,maxn=1e5+5;
const int lim=5e8;
struct Tree
{
int ch[2],siz;
}S1[maxm],S2[maxm];
struct Edge
{
int to,nxt;
}edge[maxn*2];
int n,S1tot,S2tot,tot,tp,q,la;
int val[maxn],head[maxn],fa[maxn],sz[maxn],rt1[maxn],rt2[maxn],f[maxn][25],sum[maxn],dep[maxn];
void add(int x,int y)
{
tot++;
edge[tot].to=y;
edge[tot].nxt=head[x];
head[x]=tot;
}
int findroot(int x){return fa[x]==x?x:fa[x]=findroot(fa[x]);}
void insert(Tree *tree,int &tot,int &p,int l,int r,int x)
{
tree[++tot]=tree[p];
p=tot;
if(l==r){tree[p].siz++;return ;}
int mid=l+r>>1;
if(mid>=x) insert(tree,tot,lch(p),l,mid,x);
else insert(tree,tot,rch(p),mid+1,r,x);
tree[p].siz=tree[lch(p)].siz+tree[rch(p)].siz;
}
int query(Tree *tree,int p,int lp,int l,int r,int lx,int rx)
{
if(lx<=l&&r<=rx) return tree[p].siz-tree[lp].siz;
if(rx<l||r<lx) return 0;
int mid=l+r>>1;
return query(tree,lch(p),lch(lp),l,mid,lx,rx)+query(tree,rch(p),rch(lp),mid+1,r,lx,rx);
}
void dfs(int u,int fa,int d)
{
dep[u]=d;
f[u][0]=fa;
for(int i=1;i<=20;i++) f[u][i]=f[f[u][i-1]][i-1];
rt1[u]=rt1[fa],rt2[u]=rt2[fa];
sum[u]=sum[fa]+val[u];
insert(S1,S1tot,rt1[u],-lim,lim,sum[u]+val[u]);
insert(S2,S2tot,rt2[u],-lim,lim,2*val[u]-sum[u]);
for(int i=head[u];i;i=edge[i].nxt)
{
int v=edge[i].to;
if(v==fa) continue;
dfs(v,u,d+1);
}
}
int Lca(int u,int v)
{
if(dep[u]<dep[v]) swap(u,v);
for(int i=20;i>=0;i--) if(dep[f[u][i]]>=dep[v]) u=f[u][i];
if(u==v) return u;
for(int i=20;i>=0;i--) if(f[u][i]!=f[v][i]) u=f[u][i],v=f[v][i];
return f[u][0];
}
int main()
{
scanf("%d",&tp);
scanf("%d%d",&n,&q);
for(int i=1;i<=n;i++)
{
scanf("%d",&val[i]);
fa[i]=i,sz[i]=1,sum[i]=val[i],dep[i]=1;
insert(S1,S1tot,rt1[i],-lim,lim,val[i]*2);
insert(S2,S2tot,rt2[i],-lim,lim,val[i]);
}
while(q--)
{
int op,u,v;
scanf("%d%d%d",&op,&u,&v);
if(tp) u=u^la,v=v^la;
if(!op)
{
int fu=findroot(u),fv=findroot(v);
if(sz[fu]<sz[fv]) swap(u,v),swap(fu,fv);
fa[fv]=fu,sz[fu]+=sz[fv];
dfs(v,u,dep[u]+1);
add(u,v);
add(v,u);
}
else
{
int lca=Lca(u,v);
int z=f[lca][0];
la=query(S1,rt1[u],rt1[z],-lim,lim,-lim,sum[u])+query(S2,rt2[v],rt2[lca],-lim,lim,-lim,sum[u]-sum[lca]-sum[z]);
printf("%d\n",la);
}
}
}
分类:
多校联测
, 数据结构 / 线段树 / 主席树/可持久化线段树
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现