zoukankan      html  css  js  c++  java
  • 【bzoj 2870】 最长道路tree

    题目

    边分治

    边分和点分相比就是找到一条重心边,考虑所有经过这条边的路径,之后断开这条边分成两个联通块,继续分治

    由于每次分治重心是一条边,所以只会产生两个联通块,考虑两个联通块显然要比像点分那样考虑多个联通块容易

    但是边分有一个问题,就是遇到菊花图就自闭了,复杂度变成了(O(n^2))

    我们注意到边分的复杂度还和每个点的度数有关系,于是我们建一些虚点和虚边,把这棵树变成一棵二叉树,这样复杂度就是(O(noperatorname{logn}))

    具体做法就是,一旦发现一个节点有多与两个儿子,就新建两个虚点,之后把这些儿子按照奇偶性分给两个儿子

    对于这道题,虚点的点设成其父亲的点权,虚边的边权为(0),我们每次把两个联通块的内的路径拿出排序,双指针扫一扫拼一拼就好了

    代码

    #include<bits/stdc++.h>
    #define re register
    #define LL long long
    #define max(a,b) ((a)>(b)?(a):(b))
    #define min(a,b) ((a)<(b)?(a):(b))
    inline int read() {
    	char c=getchar();int x=0;while(c<'0'||c>'9') c=getchar();
    	while(c>='0'&&c<='9') x=(x<<3)+(x<<1)+c-48,c=getchar();return x;
    }
    const int maxn=2e5+5;
    struct E{int v,nxt,w;}e[maxn<<1];
    int head[maxn],n,num,sum[maxn],vis[maxn];
    int rn,S,top[2],Mnow,tax[maxn],rt,val[maxn];
    struct Seg{int l,v;}st[2][maxn];
    std::vector<int> son[maxn];LL ans;
    inline int cmp(const Seg &A,const Seg &B) {return A.v<B.v;}
    inline void add(int x,int y,int w) {
    	e[++num].v=y;e[num].nxt=head[x];head[x]=num;e[num].w=w;
    }
    void dfs1(int x,int fa) {
    	for(re int i=head[x];i;i=e[i].nxt) {
    		if(e[i].v==fa) continue;
    		son[x].push_back(e[i].v);
    		dfs1(e[i].v,x);
    	}
    }
    void getrt(int x,int fa) {
    	sum[x]=1;
    	for(re int i=head[x];i;i=e[i].nxt) {
    		if(vis[i>>1]||e[i].v==fa) continue;
    		getrt(e[i].v,x);sum[x]+=sum[e[i].v];
    		int now=max(sum[e[i].v],S-sum[e[i].v]);
    		if(now<Mnow) Mnow=now,rt=i;
    	}
    }
    void dfs2(int o,int x,int fa,int l,int v) {
    	v=min(v,val[x]);st[o][++top[o]]=(Seg){l,v};
    	for(re int i=head[x];i;i=e[i].nxt) {
    		if(e[i].v==fa||vis[i>>1]) continue;
    		dfs2(o,e[i].v,x,l+e[i].w,v);
    	}
    }
    void solve(int x,int sz) {
    	Mnow=maxn;S=sz;getrt(x,0);
    	if(Mnow==maxn) return;
    	int now=rt;vis[rt>>1]=1;
    	top[0]=0;dfs2(0,e[now].v,0,0,maxn);
    	top[1]=0;dfs2(1,e[now^1].v,0,0,maxn);
    	std::sort(st[0]+1,st[0]+top[0]+1,cmp);
    	std::sort(st[1]+1,st[1]+top[1]+1,cmp);
    	tax[top[1]]=st[1][top[1]].l;int j=1;
    	for(re int i=top[1]-1;i;--i) tax[i]=max(st[1][i].l,tax[i+1]);
    	for(re int i=1;i<=top[0];i++) {
    		while(st[1][j].v<st[0][i].v&&j<=top[1]) j++;
    		if(j<=top[1]&&st[1][j].v>=st[0][i].v) 
    			ans=max(ans,1ll*st[0][i].v*(e[now].w+st[0][i].l+tax[j]+1));
    	}
    	tax[top[0]]=st[0][top[0]].l;j=1;
    	for(re int i=top[0]-1;i;--i) tax[i]=max(st[0][i].l,tax[i+1]);
    	for(re int i=1;i<=top[1];i++) {
    		while(st[0][j].v<st[1][i].v&&j<=top[0]) j++;
    		if(j<=top[0]&&st[0][j].v>=st[1][i].v) 
    			ans=max(ans,1ll*st[1][i].v*(e[now].w+st[1][i].l+tax[j]+1));
    	}
    	int ss=sz-sum[e[now].v];
    	solve(e[now].v,sum[e[now].v]);solve(e[now^1].v,ss);
    }
    int main() {
    	n=rn=read();
    	for(re int i=1;i<=n;i++) val[i]=read();
    	for(re int x,y,i=1;i<n;i++) 
    		x=read(),y=read(),add(x,y,0),add(y,x,0);
    	dfs1(1,0);
    	num=1;memset(head,0,sizeof(head));
    	for(re int i=1;i<=n;i++) {
    		int sz=son[i].size();
    		if(!sz) continue;
    		if(sz==1) {
    			add(i,son[i][0],son[i][0]<=rn);
    			add(son[i][0],i,son[i][0]<=rn);
    			continue;
    		}
    		if(sz==2) {
    			add(i,son[i][0],son[i][0]<=rn);add(son[i][0],i,son[i][0]<=rn);
    			add(i,son[i][1],son[i][1]<=rn);add(son[i][1],i,son[i][1]<=rn);
    			continue;
    		}
    		int t1=++n,t2=++n;val[t1]=val[t2]=val[i];
    		add(i,t1,0),add(t1,i,0);add(i,t2,0),add(t2,i,0);
    		for(re int j=0;j<son[i].size();j++) 
    			if(j&1) son[t1].push_back(son[i][j]);
    				else son[t2].push_back(son[i][j]);
    	}
    	solve(1,n);
    	std::cout<<ans;
    }
    
  • 相关阅读:
    C#遍历DataSet中数据的几种方法总结
    angularjs作用域
    SQL 语句日期用法及函数
    Sublime Text3 快捷键汇总
    AngularJS 最常用的功能
    C#中如何排除/过滤/清空/删除掉字符串数组中的空字符串
    AngularJS的指令用法
    [WCF REST] Web消息主体风格(Message Body Style)
    泛型
    语法补充
  • 原文地址:https://www.cnblogs.com/asuldb/p/11299968.html
Copyright © 2011-2022 走看看