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

简要题意:

给你一棵有根树,每个点 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
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值