5.树上问题
树上问题
开题顺序:
CF600E Lomsat gelral
CF708C Centroids
CF1706E Qpwoeirut and Vertices
luogu P2491 [SDOI2011] 消防
-
选择直径上的边一定不劣。
-
因为边权没有负数,所以用两遍
求出任意一条直径。设其上面的节点分别为 。 -
设
表示从 出发,不经过所选出的直径上的点能到达的距离最远的点的距离。 -
此时
对答案的贡献为 。最终的式子显然是 。 -
而在
固定时, 越靠近 越优,双指针维护即可。 -
单调队列 或者 表 维护暂且不提,考虑关于 的一些其他性质。 -
因为都是直径上的端点,容易有
且 。 -
而
,故最终结果可以改写为 。点击查看代码
struct node { int nxt,to,w; }e[600010]; int head[600010],dis[600010],fa[600010],c[600010],vis[600010],sum[600010],f[600010],d[600010],cnt=0; void add(int u,int v,int w) { cnt++; e[cnt].nxt=head[u]; e[cnt].to=v; e[cnt].w=w; head[u]=cnt; } void dfs1(int x,int father,int w) { c[x]=w; fa[x]=father; dis[x]=dis[father]+w; for(int i=head[x];i!=0;i=e[i].nxt) { if(e[i].to!=father) { dfs1(e[i].to,x,e[i].w); } } } void dfs2(int x) { vis[x]=1; for(int i=head[x];i!=0;i=e[i].nxt) { if(vis[e[i].to]==0) { dfs2(e[i].to); f[x]=max(f[x],f[e[i].to]+e[i].w); } } } int main() { int n,s,u,v,w,rt1=0,rt2=0,ans=0x7f7f7f7f,maxx=0,i,j; cin>>n>>s; for(i=1;i<=n-1;i++) { cin>>u>>v>>w; add(u,v,w); add(v,u,w); } dfs1(1,0,0); for(i=1;i<=n;i++) { rt1=(dis[i]>dis[rt1])?i:rt1; } dfs1(rt1,0,0); for(i=1;i<=n;i++) { rt2=(dis[i]>dis[rt2])?i:rt2; } while(rt2!=0) { vis[rt2]=1; d[0]++; d[d[0]]=rt2; rt2=fa[rt2]; } reverse(d+1,d+1+d[0]); for(i=1;i<=d[0];i++) { dfs2(d[i]); maxx=max(maxx,f[d[i]]); sum[i]=sum[i-1]+c[d[i]]; } for(i=j=1;i<=d[0];i++) { while(j+1<=d[0]&&sum[j+1]-sum[i]<=s) { j++; } ans=min(ans,max(maxx,max(sum[i],sum[d[0]]-sum[j]))); } cout<<ans<<endl; return 0; }
luogu P4253 [SCOI2015] 小凸玩密室
luogu P8990 [北大集训 2021] 小明的树
BZOJ3786 星系探索
-
貌似是伪
板子。 -
考虑维护整棵树的欧拉序列,设点
的进栈序为 ,出栈序为 ,进栈时的贡献为 ,出栈时的贡献为 。 -
操作
的链求和就转化成了 的区间求和,且容易扩展至树上任意两点间求和。 -
操作
的子树求和直接对着区间打懒惰标记。 -
操作
的换父亲可以转化为 接到新父亲的入栈序 后面,平衡树维护序列即可。 -
在使用
时因为存在换父亲操作,原序列中的顺序可能发生了改变,需要先查出其 再进行分裂。具体地,从 往上跳父亲,沿途加上左子树的大小加 。点击查看代码
struct node { ll nxt,to; }e[200010]; ll head[200010],w[200010],in[200010],out[200010],cnt=0,tot=0; void add(ll u,ll v) { cnt++; e[cnt].nxt=head[u]; e[cnt].to=v; head[u]=cnt; } struct BST { ll rt_sum,root; struct FHQ_Treap { ll son[2],fa,val,rnd,siz,dir,len,sum,lazy; }tree[200010]; #define lson(rt) (tree[rt].son[0]) #define rson(rt) (tree[rt].son[1]) #define fa(rt) (tree[rt].fa) BST() { rt_sum=root=0; srand(time(0)); } void pushup(ll rt) { tree[rt].siz=tree[lson(rt)].siz+tree[rson(rt)].siz+1; tree[rt].len=tree[lson(rt)].len+tree[rson(rt)].len+tree[rt].dir; tree[rt].sum=tree[lson(rt)].sum+tree[rson(rt)].sum+tree[rt].val; if(lson(rt)!=0) { fa(lson(rt))=rt; } if(rson(rt)!=0) { fa(rson(rt))=rt; } } ll build_rt(ll val,ll dir) { rt_sum++; lson(rt_sum)=rson(rt_sum)=tree[rt_sum].lazy=0; tree[rt_sum].val=tree[rt_sum].sum=val; tree[rt_sum].len=tree[rt_sum].dir=dir; tree[rt_sum].rnd=rand(); tree[rt_sum].siz=1; return rt_sum; } void pushlazy(ll rt,ll lazy) { tree[rt].lazy+=lazy; tree[rt].val+=tree[rt].dir*lazy; tree[rt].sum+=tree[rt].len*lazy; } void pushdown(ll rt) { if(rt!=0&&tree[rt].lazy!=0) { pushlazy(lson(rt),tree[rt].lazy); pushlazy(rson(rt),tree[rt].lazy); tree[rt].lazy=0; } } void split(ll rt,ll k,ll &ls,ll &rs) { if(rt==0) { ls=rs=0; return; } pushdown(rt); if(tree[lson(rt)].siz+1<=k) { ls=rt; split(rson(ls),k-tree[lson(rt)].siz-1,rson(ls),rs); } else { rs=rt; split(lson(rs),k,ls,lson(rs)); } pushup(rt); } ll merge(ll rt1,ll rt2) { if(rt1==0||rt2==0) { return rt1+rt2; } pushdown(rt1); pushdown(rt2); if(tree[rt1].rnd<tree[rt2].rnd) { rson(rt1)=merge(rson(rt1),rt2); pushup(rt1); return rt1; } else { lson(rt2)=merge(rt1,lson(rt2)); pushup(rt2); return rt2; } } void insert(ll val,ll dir) { root=merge(root,build_rt(val,dir)); } ll query_rk(ll rt) { ll ans=tree[lson(rt)].siz+1; for(;fa(rt)!=0;rt=fa(rt)) { ans+=(rson(fa(rt))==rt)*(tree[lson(fa(rt))].siz+1); } return ans; } void change_fa(ll x,ll y) { ll ls,rs,mid; split(root,query_rk(out[x]),ls,rs); fa(ls)=fa(rs)=0;//记得更改父亲 split(ls,query_rk(in[x])-1,ls,mid); fa(ls)=fa(mid)=0; root=merge(ls,rs); fa(root)=0; split(root,query_rk(in[y]),ls,rs); root=merge(merge(ls,mid),rs); fa(root)=0; } void update(ll x,ll val) { ll ls,rs,mid; split(root,query_rk(out[x]),ls,rs); fa(ls)=fa(rs)=0; split(ls,query_rk(in[x])-1,ls,mid); fa(ls)=fa(mid)=0; pushlazy(mid,val); root=merge(merge(ls,mid),rs); fa(root)=0; } ll query(ll x) { ll ls,rs,ans; split(root,query_rk(in[x]),ls,rs); ans=tree[ls].sum; root=merge(ls,rs); return ans; } }T; void dfs(ll x) { tot++; in[x]=tot; T.insert(w[x],1); for(ll i=head[x];i!=0;i=e[i].nxt) { dfs(e[i].to); } tot++; out[x]=tot; T.insert(-w[x],-1); } int main() { // #define Isaac #ifdef Isaac freopen("in.in","r",stdin); freopen("out.out","w",stdout); #endif ll n,m,u,v,i; char pd; cin>>n; for(i=2;i<=n;i++) { cin>>u; add(u,i); } for(i=1;i<=n;i++) { cin>>w[i]; } dfs(1); cin>>m; for(i=1;i<=m;i++) { cin>>pd>>u; if(pd=='Q') { cout<<T.query(u)<<endl; } if(pd=='C') { cin>>v; T.change_fa(u,v); } if(pd=='F') { cin>>v; T.update(u,v); } } return 0; }
CF825G Tree Queries
-
设
表示从 间的最小边权,询问等价于求 。 -
不妨钦定第一个黑色节点为根节点,上式等价于
。 -
发现只有黑点会发现影响,直接修改即可。
点击查看代码
struct node { int nxt,to; }e[2000010]; int head[2000010],dis[2000010],cnt=0; void add(int u,int v) { cnt++; e[cnt].nxt=head[u]; e[cnt].to=v; head[u]=cnt; } void dfs(int x,int fa) { dis[x]=(fa==0)?x:min(dis[fa],x); for(int i=head[x];i!=0;i=e[i].nxt) { if(e[i].to!=fa) { dfs(e[i].to,x); } } } int main() { int n,m,u,v,pd,x,rt=0,ans=0,minn=0x7f7f7f7f,i; scanf("%d%d",&n,&m); for(i=1;i<=n-1;i++) { scanf("%d%d",&u,&v); add(u,v); add(v,u); } for(i=1;i<=m;i++) { scanf("%d%d",&pd,&x); x=(ans+x)%n+1; if(pd==1) { if(rt==0) { rt=x; dfs(rt,0); } minn=min(minn,dis[x]); } else { ans=min(minn,dis[x]); printf("%d\n",ans); } } return 0; }
CF1381D The Majestic Brown Tree Snake
luogu P2664 树上游戏
luogu P5305 [GXOI/GZOI2019] 旧词
-
将询问离线下来,进行扫描线。
-
若
的话就是 luogu P4211 [LNOI2014] LCA 了。 -
仍考虑原题中从根节点到
的路径上的点打标记进行树上差分的做法。 -
具体地,设
的权值为 ,那么 路径上的点权和就是 。 -
在移动右指针
的过程中,需要将 路径上点 的权值都加上对应的 。线段树维护每个区间被加了几遍即可(也可以当历史版本和线段树维护最后一个版本和来做)。点击查看代码
const ll p=998244353; struct node { ll nxt,to; }e[500010]; ll head[500010],fa[500010],siz[500010],son[500010],dep[500010],mi[500010],c[500010],cc[500010],top[500010],dfn[500010],ans[500010],cnt=0,tot=0; vector<pair<ll,ll> >q[500010]; void add(ll u,ll v) { cnt++; e[cnt].nxt=head[u]; e[cnt].to=v; head[u]=cnt; } ll qpow(ll a,ll b,ll p) { ll ans=1; while(b) { if(b&1) { ans=ans*a%p; } b>>=1; a=a*a%p; } return ans; } void dfs1(ll x,ll k) { siz[x]=1; dep[x]=dep[fa[x]]+1; mi[x]=qpow(dep[x],k,p); for(ll i=head[x];i!=0;i=e[i].nxt) { dfs1(e[i].to,k); siz[x]+=siz[e[i].to]; son[x]=(siz[e[i].to]>siz[son[x]])?e[i].to:son[x]; } } void dfs2(ll x,ll id) { top[x]=id; tot++; dfn[x]=tot; cc[tot]=c[x]; if(son[x]!=0) { dfs2(son[x],id); for(ll i=head[x];i!=0;i=e[i].nxt) { if(e[i].to!=son[x]) { dfs2(e[i].to,e[i].to); } } } } struct SMT { struct SegementTree { ll sum,lazy,val; }tree[2000010]; ll lson(ll x) { return x*2; } ll rson(ll x) { return x*2+1; } void pushup(ll rt) { tree[rt].sum=(tree[lson(rt)].sum+tree[rson(rt)].sum)%p; } void build(ll rt,ll l,ll r) { if(l==r) { tree[rt].val=cc[l]; return; } ll mid=(l+r)/2; build(lson(rt),l,mid); build(rson(rt),mid+1,r); tree[rt].val=(tree[lson(rt)].val+tree[rson(rt)].val)%p; } void pushdown(ll rt) { if(tree[rt].lazy!=0) { tree[lson(rt)].sum=(tree[lson(rt)].sum+tree[rt].lazy*tree[lson(rt)].val%p)%p; tree[rson(rt)].sum=(tree[rson(rt)].sum+tree[rt].lazy*tree[rson(rt)].val%p)%p; tree[lson(rt)].lazy+=tree[rt].lazy; tree[rson(rt)].lazy+=tree[rt].lazy; tree[rt].lazy=0; } } void update(ll rt,ll l,ll r,ll x,ll y,ll val) { if(x<=l&&r<=y) { tree[rt].sum=(tree[rt].sum+val*tree[rt].val%p)%p; tree[rt].lazy+=val; return; } pushdown(rt); ll mid=(l+r)/2; if(x<=mid) { update(lson(rt),l,mid,x,y,val); } if(y>mid) { update(rson(rt),mid+1,r,x,y,val); } pushup(rt); } ll query(ll rt,ll l,ll r,ll x,ll y) { if(x<=l&&r<=y) { return tree[rt].sum; } pushdown(rt); ll mid=(l+r)/2,ans=0; if(x<=mid) { ans=(ans+query(lson(rt),l,mid,x,y))%p; } if(y>mid) { ans=(ans+query(rson(rt),mid+1,r,x,y))%p; } return ans; } }T; void update(ll u,ll v,ll val,ll n) { while(top[u]!=top[v]) { if(dep[top[u]]>dep[top[v]]) { T.update(1,1,n,dfn[top[u]],dfn[u],val); u=fa[top[u]]; } else { T.update(1,1,n,dfn[top[v]],dfn[v],val); v=fa[top[v]]; } } if(dep[u]>dep[v]) { T.update(1,1,n,dfn[v],dfn[u],val); } else { T.update(1,1,n,dfn[u],dfn[v],val); } } ll query(ll u,ll v,ll n) { ll ans=0; while(top[u]!=top[v]) { if(dep[top[u]]>dep[top[v]]) { ans=(ans+T.query(1,1,n,dfn[top[u]],dfn[u]))%p; u=fa[top[u]]; } else { ans=(ans+T.query(1,1,n,dfn[top[v]],dfn[v]))%p; v=fa[top[v]]; } } if(dep[u]>dep[v]) { ans=(ans+T.query(1,1,n,dfn[v],dfn[u]))%p; } else { ans=(ans+T.query(1,1,n,dfn[u],dfn[v]))%p; } return ans; } int main() { ll n,m,k,x,y,i,j; cin>>n>>m>>k; for(i=2;i<=n;i++) { cin>>fa[i]; add(fa[i],i); } for(i=1;i<=m;i++) { cin>>x>>y; q[x].push_back(make_pair(y,i)); } dfs1(1,k); for(i=1;i<=n;i++) { c[i]=(mi[i]-mi[fa[i]]+p)%p; } dfs2(1,1); T.build(1,1,n); for(i=1;i<=n;i++) { update(i,1,1,n); for(j=0;j<q[i].size();j++) { ans[q[i][j].second]=query(q[i][j].first,1,n); } } for(i=1;i<=m;i++) { cout<<ans[i]<<endl; } return 0; }
luogu P5306 [COCI2018-2019#5] Transport
本文来自博客园,作者:hzoi_Shadow,原文链接:https://www.cnblogs.com/The-Shadow-Dragon/p/18470527,未经允许严禁转载。
版权声明:本作品采用 「署名-非商业性使用-相同方式共享 4.0 国际」许可协议(CC BY-NC-SA 4.0) 进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现
2023-11-04 B3610 [图论与代数结构 801] 无向图的块 题解