zoukankan      html  css  js  c++  java
  • 【BZOJ2870】最长道路

    权限题

    题意

    给出一棵树,点有点权,找到树上的一条路径使得路径上点的个数和其中点权最小的点的点权之积最大,输出最大值。

    Sol

    边分治板子题啦。

    边分治后对于分出来的两棵子树 , 按到左右根的最小点权排序后直接用单调指针对每一个点找到另一棵树中的最优点即可。

    code:

    #include<bits/stdc++.h>
    using namespace std;
    #define Set(a,b) memset(a,b,sizeof(a))
    template<class T>inline void init(T&x){
    	x=0;char ch=getchar();bool t=0;
    	for(;ch>'9'||ch<'0';ch=getchar()) if(ch=='-') t=1;
    	for(;ch>='0'&&ch<='9';ch=getchar()) x=(x<<1)+(x<<3)+(ch-48);
    	if(t) x=-x;return;
    }
    int n,N;
    typedef long long ll;
    typedef vector<int> ary;
    const int MAXN=2e5+10;// 4 倍空间 , 类似线段树
    int val[MAXN];
    ary son[MAXN];
    struct edge{
    	int to,next,w;
    }a[MAXN<<1];
    int head[MAXN],cnt=0;
    inline void add(int x,int y,int z){a[cnt]=(edge){y,head[x],z};head[x]=cnt++;}
    inline void add_edge(int x,int y,int z){add(x,y,z),add(y,x,z);}
    void dfs(int u,int fa){
    	for(int v,i=head[u];~i;i=a[i].next)
    		if((v=a[i].to)!=fa) son[u].push_back(v),dfs(v,u);
    	return;
    }
    inline void Rebuild(){// 重新建树
    	Set(head,-1);cnt=0;
    	for(int i=1;i<=n;++i) {
    		int snum=son[i].size();
    		if(snum<=2) {
    			for(int j=0;j<snum;++j) {
    				add_edge(i,son[i][j],son[i][j]<=N);// 连向虚点的边权是 0
    			}
    		}
    		else {
    			int sl=++n,sr=++n;//新建虚点左右儿子来管理所有的儿子节点
    			val[sl]=val[sr]=val[i];// 点权和父亲一样
    			add_edge(i,sl,0),add_edge(i,sr,0);
    			for(int j=0;j<snum;++j)
    				(j&1)? son[sl].push_back(son[i][j]):son[sr].push_back(son[i][j]);// 各分一半递归建树
    		}
    	}
    	return;
    }
    int vis[MAXN];
    int mx,size[MAXN],Ced,SZ;
    ll ans;
    void Find(int u,int fa){
    	size[u]=1;
    	for(int v,i=head[u];~i;i=a[i].next){
    		v=a[i].to;if(vis[i>>1]||v==fa) continue;
    		Find(v,u);
    		size[u]+=size[v];
    		int dat=max(size[v],SZ-size[v]);
    		if(dat<mx) mx=dat,Ced=i;
    	}
    	return;
    }
    typedef pair<int,int> PA;
    PA S[2][MAXN];int top[2];
    void dfs(int u,int fa,PA*S,int&top,int dis,int Mi){
    	Mi=min(Mi,val[u]);S[++top]=PA(Mi,dis);
    	for(int v,i=head[u];~i;i=a[i].next){
    		v=a[i].to;if(v==fa||vis[i>>1]) continue;
    		dfs(v,u,S,top,dis+a[i].w,Mi);
    	}
    	return;
    }
    
    void Divide(int u) {
    	mx=1e9;Find(u,0);//找中心边
    	if(mx>=1e9) return;vis[Ced>>1]=1;
    	top[0]=top[1]=0;
    	dfs(a[Ced].to,0,S[0],top[0],0,1e9);
    	dfs(a[Ced^1].to,0,S[1],top[1],0,1e9);
    	int upd=a[Ced].w+1;
    	sort(S[0]+1,S[0]+1+top[0]);
    	sort(S[1]+1,S[1]+1+top[1]);
    	int p=top[1];int maxlen=0;// 用单调指针维护即可
    	for(int i=top[0];i;--i) {
    		int val=S[0][i].first,dis=S[0][i].second;
    		while(p&&S[1][p].first>=val) maxlen=max(maxlen,S[1][p--].second);
    		if(p<top[1]) ans=max(ans,(ll)(maxlen+dis+upd)*val);
    	}
    	p=top[0];maxlen=0;
    	for(int i=top[1];i;--i) {
    		int val=S[1][i].first,dis=S[1][i].second;
    		while(p&&S[0][p].first>=val) maxlen=max(maxlen,S[0][p--].second);
    		if(p<top[0]) ans=max(ans,(ll)(maxlen+dis+upd)*val);
    	}
    	int sl=a[Ced].to,sr=a[Ced^1].to;
    	int tot=SZ,szl=size[sl];
    	SZ=szl;Divide(sl);
    	SZ=tot-szl;Divide(sr);
    	return;
    }
    int main()
    {
    	init(n),N=n;Set(head,-1);ans=0;
    	for(int i=1;i<=n;++i) init(val[i]),ans=max(ans,(ll)val[i]);
    	int u,v;
    	for(int i=1;i<n;++i) {
    		init(u),init(v);
    		add(u,v,1);add(v,u,1);
    	}
    	dfs(1,0);Rebuild();SZ=n;Ced=0;Divide(1);
    	cout<<ans<<endl;
    	return 0;
    }
    
    
  • 相关阅读:
    hdu 1290 献给杭电五十周年校庆的礼物 (DP)
    hdu 3123 GCC (数学)
    hdu 1207 汉诺塔II (DP)
    hdu 1267 下沙的沙子有几粒? (DP)
    hdu 1249 三角形 (DP)
    hdu 2132 An easy problem (递推)
    hdu 2139 Calculate the formula (递推)
    hdu 1284 钱币兑换问题 (DP)
    hdu 4151 The Special Number (DP)
    hdu 1143 Tri Tiling (DP)
  • 原文地址:https://www.cnblogs.com/NeosKnight/p/10519332.html
Copyright © 2011-2022 走看看