• LOJ#6463 AK YOI 树分治+线段树合并


    传送门

    既然是树上路径统计问题,不难想到要使用树分治,这里以点分治为例

    由点分治的性质,每层只需要考虑经过重心的路径

    因为需要维护路径长度在一定范围内的最大权值和,所以要用一个数据结构维护一下到根节点距离在一定范围内的最大权值和

    显然线段树是一个不错的选择,对每个子树建立一个线段树,根节点的答案用每个子树的线段树都更新一遍即可

    考虑更新子树中的点的答案,这时需要使用除这棵子树外的所有子树的线段树一起更新

    我们可以使用线段树合并来维护,给子树任意确定一个顺序,然后通过维护每个子树的前缀和后缀线段树的并即可快速得到除去某棵子树后的线段树

    显然复杂度是有保证的,因为线段树合并的复杂度无论如何都不会高于暴力插入三遍

    总复杂度(O(nlog^2 n)),常数应该不小,不过跑的挺快233333

    #include<bits/stdc++.h>
    using namespace std;
    const int maxn=100005,maxm=maxn*100;
    const long long INF=0x5f5f5f5f5f5f5f5fll;
    void solve(int,int);
    int getcenter(int,int);
    int getdis(int);
    void getans(int,int);
    void modify(int,int,int&);
    int merge(int,int);
    long long query(int,int,int);
    long long mx[maxm];
    int lc[maxm],rc[maxm],cnt=0,root[maxn],prefix[maxn],suffix[maxn];
    vector<int>G[maxn];
    bool vis[maxn];
    long long w[maxn],ans[maxn],ant[maxn],tmp;
    int p[maxn],size[maxn],son[maxn],q[maxn],d[maxn],pr[maxn],nx[maxn];
    int n,m,L,R,val[maxn],s,t;
    int main(){
    	mx[0]=-INF;
    	scanf("%d%d%d",&n,&L,&R);
    	for(int i=1;i<=n;i++){
    		scanf("%d",&val[i]);
    		ans[i]=-3472328296227680305ll;
    	}
    	for(int i=1,x,y;i<n;i++){
    		scanf("%d%d",&x,&y);
    		G[x].push_back(y);
    		G[y].push_back(x);
    	}
    	solve(1,n);
    	for(int i=1;i<=n;i++){
    		if(i>1)printf(" ");
    		printf("%lld",ans[i]);
    	}
    	printf("
    ");
    	return 0;
    }
    void solve(int x,int sz){
    	x=getcenter(x,sz);
    	m=sz;
    	vis[x]=true;
    	w[x]=val[x];
    	d[x]=0;
    	if(sz==1)return;
    	s=0;
    	tmp=w[x];
    	modify(0,m,root[x]);
    	for(int i=0;i<(int)G[x].size();i++)
    		if(!vis[G[x][i]]){
    			p[G[x][i]]=x;
    			getdis(G[x][i]);
    		}
    	s=L;t=R;
    	for(int i=0,last=0;i<(int)G[x].size();i++)
    		if(!vis[G[x][i]]){
    			if(s<=m)ans[x]=max(ans[x],query(0,m,root[G[x][i]]));
    			prefix[G[x][i]]=merge(prefix[last],root[G[x][i]]);
    			pr[G[x][i]]=last;
    			last=G[x][i];
    		}
    	ant[x]=-INF;
    	for(int i=(int)G[x].size()-1,last=0;~i;i--)
    		if(!vis[G[x][i]]){
    			suffix[G[x][i]]=merge(suffix[last],root[G[x][i]]);
    			nx[G[x][i]]=last;
    			last=G[x][i];
    		}
    	for(int i=0;i<(int)G[x].size();i++)
    		if(!vis[G[x][i]])getans(G[x][i],val[x]);
    	ans[x]=max(ans[x],ant[x]);
    	root[x]=0;
    	for(int i=0;i<(int)G[x].size();i++)
    		if(!vis[G[x][i]]){
    			pr[G[x][i]]=nx[G[x][i]]=0;
    			root[G[x][i]]=prefix[G[x][i]]=suffix[G[x][i]]=0;
    		}
    	cnt=0;
    	for(int i=0;i<(int)G[x].size();i++)
    		if(!vis[G[x][i]])solve(G[x][i],size[G[x][i]]);
    }
    int getcenter(int x,int s){
    	int head=0,tail=0;
    	q[tail++]=x;
    	while(head!=tail){
    		x=q[head++];
    		size[x]=1;
    		son[x]=0;
    		for(int i=0;i<(int)G[x].size();i++)
    			if(!vis[G[x][i]]&&G[x][i]!=p[x]){
    				p[G[x][i]]=x;
    				q[tail++]=G[x][i];
    			}
    	}
    	for(int i=tail-1;i;i--){
    		x=q[i];
    		size[p[x]]+=size[x];
    		if(size[x]>size[son[p[x]]])son[p[x]]=x;
    	}
    	x=q[0];
    	while(son[x]&&size[son[x]]>(s>>1))x=son[x];
    	return x;
    }
    int getdis(int x){
    	int head=0,tail=0,rt=x;
    	q[tail++]=x;
    	while(head!=tail){
    		x=q[head++];
    		s=d[x]=d[p[x]]+1;
    		tmp=w[x]=w[p[x]]+val[x];
    		modify(0,m,root[rt]);
    		size[x]=1;
    		for(int i=0;i<(int)G[x].size();i++)
    			if(!vis[G[x][i]]&&G[x][i]!=p[x]){
    				p[G[x][i]]=x;
    				q[tail++]=G[x][i];
    			}
    	}
    	for(int i=tail-1;i;i--){
    		x=q[i];
    		size[p[x]]+=size[x];
    	}
    	return d[q[tail-1]];
    }
    void getans(int x,int v){
    	int head=0,tail=0,rt=merge(prefix[pr[x]],suffix[nx[x]]);
    	q[tail++]=x;
    	while(head!=tail){
    		x=q[head++];
     		s=L-d[x];
    		t=R-d[x];
    		if(t<0||s>m)ant[x]=-INF;
    		else ant[x]=w[x]-v+query(0,m,rt);
    		if(s<=0&&t>=0)ant[x]=max(ant[x],w[x]);
    		for(int i=0;i<(int)G[x].size();i++)
    			if(!vis[G[x][i]]&&G[x][i]!=p[x]){
    				p[G[x][i]]=x;
    				q[tail++]=G[x][i];
    			}
    	}
    	for(int i=tail-1;~i;i--){
    		x=q[i];
    		ant[p[x]]=max(ant[p[x]],ant[x]);
    		ans[x]=max(ans[x],ant[x]);
    	}
    }
    void modify(int l,int r,int &rt){
    	if(!rt){
    		rt=++cnt;
    		mx[rt]=-INF;
    		lc[rt]=rc[rt]=0;
    	}
    	mx[rt]=max(mx[rt],tmp);
    	if(l==r)return;
    	int mid=(l+r)>>1;
    	if(s<=mid)modify(l,mid,lc[rt]);
    	else modify(mid+1,r,rc[rt]);
    }
    int merge(int x,int y){
    	if(!x||!y)return x|y;
    	int z=++cnt;
    	mx[z]=max(mx[x],mx[y]);
    	lc[z]=merge(lc[x],lc[y]);
    	rc[z]=merge(rc[x],rc[y]);
    	return z;
    }
    long long query(int l,int r,int rt){
    	if(s<=l&&t>=r)return mx[rt];
    	int mid=(l+r)>>1;
    	if(t<=mid)return query(l,mid,lc[rt]);
    	if(s>mid)return query(mid+1,r,rc[rt]);
    	return max(query(l,mid,lc[rt]),query(mid+1,r,rc[rt]));
    }
    
  • 相关阅读:
    Java8新特性-日期时间
    解决有道云笔记导入md文件无法加载文件内的图片方式
    Mac安装Navicat Premium 12 永久破解
    MacBook Pro安装和配置Tomcat
    MySQL不支持DELETE使用表别名?
    JAVA设计模式之模板方法
    Lombok中的@Builder注解
    JAVA设计模式之策略模式
    Stream中的Peek操作
    MySql插入一条数据不提交事务主键仍自增的理解
  • 原文地址:https://www.cnblogs.com/hzoier/p/9383245.html
Copyright © 2020-2023  润新知