【校内模拟】层层回忆(线段树合并)

mac2025-07-17  4

简要题意:

给你一棵有根树,每个点 u u u有正数权值 a u a_u au,定义一个点 u u u的权值 f u f_u fu为其子树中点的 a a a之和。

对于两个点 u , v u,v u,v,我们称 u u u能到达 v v v当且仅当 f u f_u fu与该路径上 f f f的最大值和最小值都不相等。

对于每个点 u u u,请你求出所有它能达到的所有点的 f f f值按位或的结果。

题解:

如果是异或可能会好做一点。

由于是按位或而不是异或,没有逆运算,我们能够利用的只有交换律和结合律。

首先注意到由于所有 a u a_u au都是正数,也就意味着任何一个点到根的路径上的 f f f单调递增。

容易注意到一个点不能往子树走,因为这样它一定是路径上最大值。

那么任何具有祖先后代关系的节点都不能互相到达(这什么破规定),那么考虑两个没有祖先后代关系的点。

容易注意到 L C A LCA LCA就是路径上权值最大的点,则两个没有祖先后代关系的点 u , v u,v u,v u u u能到达 v v v当且仅当 f u > f v f_u>f_v fu>fv,即 f u f_u fu不是路径上最小值。

剩下的就和 PKUWC 2018 Minimax 的线段树合并套路一样了。

建立权值线段树,从左到右合并的同时维护一下当前位或额结果。 注意到我们需要在线段树叶子节点维护点的集合并且需要支持打标记和快速合并,采用平衡树或者可并堆的策略合并即可。

全场只有我一个人写的线段树合并。。。

然而跑得比几乎所有的树状数组都快。

时间复杂度上界 O ( n log ⁡ n ) O(n\log n) O(nlogn)不满,下界好像很低但不是很清楚,由于常数问题还是比不过一些树状数组。


代码:

#include<bits/stdc++.h> #define ll long long #define re register #define cs const namespace IO{ inline char gc(){ static cs int Rlen=1<<22|1; static char buf[Rlen],*p1,*p2; return (p1==p2)&&(p2=(p1=buf)+fread(buf,1,Rlen,stdin),p1==p2)?EOF:*p1++; }template<typename T>inline T get(){ char c;T num; while(!isdigit(c=gc()));num=c^48; while(isdigit(c=gc()))num=(num+(num<<2)<<1)+(c^48); return num; }inline int gi(){return get<int>();} } using namespace IO; using std::cerr; using std::cout; cs int N=4e5+7; int n,c,m; int g[N],f[N],rk[N],val[N]; namespace SBT{ cs int N=4e5+7; int lc[N],rc[N],tag[N],ht[N]; inline void init(int n){std::fill(ht+1,ht+n+1,1);} inline void push(int u,int vl){tag[u]|=vl;} inline void pushdown(int u){ if(tag[u]){ if(lc[u])tag[lc[u]]|=tag[u]; if(rc[u])tag[rc[u]]|=tag[u]; g[u]|=tag[u],tag[u]=0; } } inline int merge(int u,int v){ if(!u||!v)return u|v; if(ht[u]>ht[v])std::swap(u,v); pushdown(u);rc[u]=merge(rc[u],v); if(ht[rc[u]]>ht[lc[u]])std::swap(lc[u],rc[u]); ht[u]=ht[rc[u]]+1;return u; } inline void pushall(int u){pushdown(u);if(lc[u])pushall(lc[u]);if(rc[u])pushall(rc[u]);} } namespace SGT{ cs int N=::N*20; int lc[N],rc[N],tag[N],vl[N],rt[N],dl[N],tp,tot; inline int newnode(){ int u=tp?dl[tp--]:++tot; lc[u]=rc[u]=0,tag[u]=vl[u]=rt[u]=0; return u; } inline void push(int u,int vl){tag[u]|=vl;} inline void pushdown(int u){ if(tag[u]){ if(lc[u])tag[lc[u]]|=tag[u]; if(rc[u])tag[rc[u]]|=tag[u]; tag[u]=0; } } inline void pushup(int u){vl[u]=vl[lc[u]]|vl[rc[u]];} inline void ins(int &u,int l,int r,int p,int ps){ if(!u)u=newnode(); if(l==r){ vl[u]=val[l]; rt[u]=SBT::merge(rt[u],ps); return ; }int mid=l+r>>1;pushdown(u); (p<=mid)?ins(lc[u],l,mid,p,ps):ins(rc[u],mid+1,r,p,ps); pushup(u); } int pre_u,pre_v; inline int merge(int u,int v,int l,int r){ if(!u&&!v)return 0; if(!u&&v){ push(v,pre_u); pre_v|=vl[v]; return v; } if(!v&&u){ push(u,pre_v); pre_u|=vl[u]; return u; } if(l==r){ SBT::push(rt[u],tag[u]|pre_v); SBT::push(rt[v],tag[v]|pre_u); rt[u]=SBT::merge(rt[u],rt[v]); tag[u]=tag[v]=0; pre_u|=val[l],pre_v|=val[l]; dl[++tp]=v;return u; }int mid=l+r>>1; pushdown(u),pushdown(v); lc[u]=merge(lc[u],lc[v],l,mid); rc[u]=merge(rc[u],rc[v],mid+1,r); pushup(u);dl[++tp]=v;return u; } inline void pushall(int u,int l,int r){ if(!u)return ; if(l==r){ SBT::push(rt[u],tag[u]); SBT::pushall(rt[u]); return ; }int mid=l+r>>1;pushdown(u); pushall(lc[u],l,mid);pushall(rc[u],mid+1,r); } } int el[N],nxt[N<<1|1],to[N<<1|1],ec; inline void adde(int u,int v){ nxt[++ec]=el[u],el[u]=ec,to[ec]=v; nxt[++ec]=el[v],el[v]=ec,to[ec]=u; } void pre_dfs(int u,int p){ for(int re e=el[u],v;e;e=nxt[e]) if((v=to[e])!=p)pre_dfs(v,u),f[u]+=f[v]; } int rt[N]; void dfs(int u,int p){ for(int re e=el[u],v;e;e=nxt[e]) if((v=to[e])!=p){ dfs(v,u);SGT::pre_u=SGT::pre_v=0; rt[u]=SGT::merge(rt[u],rt[v],1,m); } SGT::ins(rt[u],1,m,rk[u],u); } signed main(){ #ifdef zxyoi freopen("forever.in","r",stdin); #else #ifndef ONLINE_JUDGE freopen("forever.in","r",stdin);freopen("forever.out","w",stdout); #else int size=200<<20; __asm__ ("movq %0,%%rsp\n"::"r"((char*)malloc(size)+size)); #endif #endif n=gi(),c=gi(); for(int re i=1;i<n;++i)adde(gi(),gi()); for(int re i=1;i<=n;++i)f[i]=gi();pre_dfs(c,0); for(int re i=1;i<=n;++i)val[i]=f[i]; std::sort(val+1,val+n+1);m=std::unique(val+1,val+n+1)-val-1; for(int re i=1;i<=n;++i)rk[i]=std::lower_bound(val+1,val+m+1,f[i])-val; SBT::init(n);dfs(c,0);SGT::pushall(rt[c],1,m); for(int re i=1;i<=n;++i)cout<<g[i]<<" "; exit(0); }
最新回复(0)