zoukankan      html  css  js  c++  java
  • 【BZOJ2870】最长道路tree 点分治+树状数组

    【BZOJ2870】最长道路tree

    Description

    H城很大,有N个路口(从1到N编号),路口之间有N-1边,使得任意两个路口都能互相到达,这些道路的长度我们视作一样。每个路口都有很多车辆来往,所以每个路口i都有一个拥挤程度v[i],我们认为从路口s走到路口t的痛苦程度为s到t的路径上拥挤程度的最小值,乘上这条路径上的路口个数所得的积。现在请你求出痛苦程度最大的一条路径,你只需输出这个痛苦程度。
    简化版描述:
    给定一棵N个点的树,求树上一条链使得链的长度乘链上所有点中的最小权值所得的积最大。
    其中链长度定义为链上点的个数。

    Input

    第一行N
    第二行N个数分别表示1~N的点权v[i]
    接下来N-1行每行两个数x、y,表示一条连接x和y的边

    Output

    一个数,表示最大的痛苦程度。

    Sample Input

    3
    5 3 5
    1 2
    1 3

    Sample Output

    10

    【样例解释】
    选择从1到3的路径,痛苦程度为min(5,5)*2=10

    HINT

    100%的数据n<=50000
    其中有20%的数据树退化成一条链
    所有数据点权<=65536
    Hint:建议答案使用64位整型

    题解:我们采用树形DP版本的点分治。对于当前的分治中心x,我们依次遍历它的每个儿子的子树,每访问到一个点y,我们记录y到x路径上的权值最小值min和长度len,然后在树状数组中找到:在以前的子树中,min>=当前min的len的最大值,然后用min*(len+当前len)更新答案。

    这样正着反着做两遍即可。

    #include <cstdio>
    #include <cstring>
    #include <iostream>
    using namespace std;
    const int maxn=50010;
    typedef long long ll;
    int n,m,cnt,now,tot,mn,rt;
    int to[maxn<<1],next[maxn<<1],head[maxn],siz[maxn],v[maxn],s[70000],vis[maxn],tim[70000],p[maxn];
    ll ans;
    inline int rd()
    {
    	int ret=0,f=1;	char gc=getchar();
    	while(gc<'0'||gc>'9')	{if(gc=='-')f=-f;	gc=getchar();}
    	while(gc>='0'&&gc<='9')	ret=ret*10+gc-'0',gc=getchar();
    	return ret*f;
    }
    void getrt(int x,int fa)
    {
    	siz[x]=1;
    	int i,tmp=0;
    	for(i=head[x];i!=-1;i=next[i])	if(!vis[to[i]]&&to[i]!=fa)	getrt(to[i],x),siz[x]+=siz[to[i]],tmp=max(tmp,siz[to[i]]);
    	tmp=max(tmp,tot-siz[x]);
    	if(tmp<mn)	rt=x,mn=tmp;
    }
    inline int query(int x)
    {
    	x++;
    	int i,ret=0;
    	for(i=x;i<=m;i+=i&-i)
    	{
    		if(tim[i]<now)	tim[i]=now,s[i]=0;
    		ret=max(ret,s[i]);
    	}
    	return ret;
    }
    inline void updata(int x,int val)
    {
    	x++;
    	for(int i=x;i;i-=i&-i)
    	{
    		if(tim[i]<now)	tim[i]=now,s[i]=0;
    		s[i]=max(s[i],val);
    	}
    }
    void ask(int x,int fa,int dep,int sn)
    {
    	sn=min(sn,v[x]),ans=max(ans,(ll)(dep+query(sn))*sn);
    	for(int i=head[x];i!=-1;i=next[i])	if(!vis[to[i]]&&to[i]!=fa)	ask(to[i],x,dep+1,sn);
    }
    void change(int x,int fa,int dep,int sn)
    {
    	sn=min(sn,v[x]),updata(sn,dep);
    	for(int i=head[x];i!=-1;i=next[i])	if(!vis[to[i]]&&to[i]!=fa)	change(to[i],x,dep+1,sn);
    }
    void solve(int x)
    {
    	vis[x]=1;
    	int i;
    	now++,p[0]=0;
    	for(i=head[x];i!=-1;i=next[i])	if(!vis[to[i]])	p[++p[0]]=to[i];
    	for(i=1;i<=p[0];i++)	ask(p[i],x,2,v[x]),change(p[i],x,1,v[x]);
    	now++;
    	for(i=p[0];i>=1;i--)	ask(p[i],x,2,v[x]),change(p[i],x,1,v[x]);
    	for(i=head[x];i!=-1;i=next[i])	if(!vis[to[i]])	tot=siz[to[i]],mn=1<<30,getrt(to[i],x),solve(rt);
    }
    inline void add(int a,int b)
    {
    	to[cnt]=b,next[cnt]=head[a],head[a]=cnt++;
    }
    int main()
    {
    	n=rd();
    	int i,a,b;
    	for(i=1;i<=n;i++)	v[i]=rd(),m=max(m,v[i]+1);
    	memset(head,-1,sizeof(head));
    	for(i=1;i<n;i++)	a=rd(),b=rd(),add(a,b),add(b,a);
    	tot=n,mn=1<<30,ans=m-1,getrt(1,0),solve(rt);
    	printf("%lld",ans);
    	return 0;
    }
  • 相关阅读:
    oracle的根容器下新建pdb容器及本地用户
    oracle监听配置与防火墙问题
    oracle问题:ORA-09817及解决办法
    Oracle:Ora-01652无法通过128(在temp表空间中)扩展temp段的过程-解决步骤
    oracle:ORA-14765建索引阻塞创建分区及处理步骤
    oracle-组合索引字段位置与查询效率之间的关系
    hbase的split策略和预分区
    启动hbase后hmaster自动关闭
    hive一级分区、二级分区、动态分区
    hive beeline连接和交互shell连接
  • 原文地址:https://www.cnblogs.com/CQzhangyu/p/7536785.html
Copyright © 2011-2022 走看看