zoukankan      html  css  js  c++  java
  • 【CF1303G】Sum of Prefix Sums(点分治+李超线段树)

    点此看题面

    • 一棵(n)个点的树,第(i)个点权值为(a_i)
    • 假设一条路径上的点权依次为(b_{1sim k}),则这条路径的权值为(sum_{i=1}^ki imes b_i)
    • 对于所有树上路径,求出其中最大的权值。
    • (nle1.5 imes10^5,a_ile10^6)

    点分治+李超线段树

    对于这种询问范围为所有树上路径问题,很容易想到点分治。

    核心问题是如何合并两条路径。

    假设一条从下往上到当前根的路径点数为(c_1),权值为(s_1);一条由当前根从上往下的路径点权和为(t_2),权值为(s_2)。那么这两条路径合并后的权值就是(s_1+s_2+c_1 imes t_2)

    也就是说,每条从上往下的路径可以看作一个一次函数(t_2x+s_2),而对于每条从下往上的路径我们询问(x=c_1)时的最大(y)值再加上(s_1)更新答案。

    显然可以套用李超线段树解决。

    代码:(O(nlog^2n))

    #include<bits/stdc++.h>
    #define Tp template<typename Ty>
    #define Ts template<typename Ty,typename... Ar>
    #define Rg register
    #define RI Rg int
    #define Cn const
    #define CI Cn int&
    #define I inline
    #define W while
    #define N 150000
    #define V 150000000000LL
    #define LL long long
    #define add(x,y) (e[++ee].nxt=lnk[x],e[lnk[x]=ee].to=y)
    using namespace std;
    int n,a[N+5],ee,lnk[N+5];struct edge {int to,nxt;}e[N<<1];
    namespace FastIO
    {
    	#define FS 100000
    	#define tc() (FA==FB&&(FB=(FA=FI)+fread(FI,1,FS,stdin),FA==FB)?EOF:*FA++)
    	char oc,FI[FS],*FA=FI,*FB=FI;
    	Tp I void read(Ty& x) {x=0;W(!isdigit(oc=tc()));W(x=(x<<3)+(x<<1)+(oc&15),isdigit(oc=tc()));}
    	Ts I void read(Ty& x,Ar&... y) {read(x),read(y...);}
    }using namespace FastIO;
    struct Seg
    {
    	LL k,b;I Seg(Cn LL& x=0,Cn LL& y=0):k(x),b(y){}
    	I LL operator () (Cn LL& x) Cn {return k*x+b;}
    };
    class SegmentTree
    {
    	private:
    		#define PT CI l=1,CI r=n,CI rt=1
    		#define LT l,mid,rt<<1
    		#define RT mid+1,r,rt<<1|1
    		Seg P[N<<2];
    	public:
    		I void Cl(PT) {if(!P[rt].k) return;if(P[rt]=Seg(),l==r) return;RI mid=l+r>>1;Cl(LT),Cl(RT);}//清空
    		I LL Q(CI x,PT) {RI mid=l+r>>1;return P[rt].k?max(l^r?(x<=mid?Q(x,LT):Q(x,RT)):0,P[rt](x)):0;}//询问x处的最大y值
    		I void A(Seg s,PT)//加入一条线段
    		{
    			if(!P[rt].k) return (void)(P[rt]=s);RI mid=l+r>>1;s(mid)>P[rt](mid)&&(swap(s,P[rt]),0);//记录较优的线段
    			if(l==r) return;s(l)>P[rt](l)&&(A(s,LT),0),s(r)>P[rt](r)&&(A(s,RT),0);//进入未被完全碾压的子区间
    		}
    }S;
    namespace T//点分治
    {
    	int u[N+5];LL ans;
    	int rt,Sz[N+5],Mx[N+5];I void GetRt(CI x,CI lst,RI sz)//找重心
    	{
    		Sz[x]=1,Mx[x]=0;for(RI i=lnk[x];i;i=e[i].nxt) !u[e[i].to]&&
    			e[i].to^lst&&(GetRt(e[i].to,x,sz),Sz[x]+=Sz[e[i].to],Mx[x]=max(Mx[x],Sz[e[i].to]));
    		(Mx[x]=max(Mx[x],sz-Sz[x]))<Mx[rt]&&(rt=x);
    	}
    	I void U(CI x,CI lst,RI c,LL t,LL s)//从上往下的路径
    	{
    		S.A(Seg(t+=a[x],s+=1LL*(++c)*a[x]));//把这个一次函数修加到线段树上
    		for(RI i=lnk[x];i;i=e[i].nxt) e[i].to^lst&&!u[e[i].to]&&(U(e[i].to,x,c,t,s),0);
    	}
    	I void Q(CI x,CI lst,RI c,LL t,LL s)//从下往上的路径
    	{
    		++c,s+=(t+=a[x]),ans=max(ans,S.Q(c)+s);//在线段树上询问
    		for(RI i=lnk[x];i;i=e[i].nxt) e[i].to^lst&&!u[e[i].to]&&(Q(e[i].to,x,c,t,s),0);
    	}
    	int T,St[N+5];I void Solve(RI x)//点分治
    	{
    		RI i;for(u[x]=1,S.Cl(),i=lnk[x];i;i=e[i].nxt) !u[e[i].to]&&//正着搞一遍,最后考虑当前根
    			(Q(St[++T]=e[i].to,x,1,a[x],a[x]),U(e[i].to,x,0,0,0),0);ans=max(ans,S.Q(1)+a[x]);
    		S.Cl(),S.A(Seg(a[x],a[x]));W(T) Q(St[T],x,0,0,0),U(St[T--],x,1,a[x],a[x]);//反着搞一遍,首先插入当前根
    		for(i=lnk[x];i;i=e[i].nxt) !u[e[i].to]&&(GetRt(e[i].to,rt=0,Sz[e[i].to]),Solve(rt),0);//进入分治子树
    	}
    }
    int main()
    {
    	RI i,x,y;for(read(n),i=1;i^n;++i) read(x,y),add(x,y),add(y,x);for(i=1;i<=n;++i) read(a[i]);
    	using namespace T;return Mx[0]=1e9,GetRt(1,rt=0,n),Solve(rt),printf("%lld
    ",ans),0;
    }
    
    败得义无反顾,弱得一无是处
  • 相关阅读:
    UIView常用的一些方法小记之setNeedsDisplay和setNeedsLayout
    initWithFrame方法的使用
    加载xib文件,如果想在初始化的时候就添加点东西就重载-(id)initWithCoder:(NSCoder *)aDecoder
    Mac os 下brew的安装与使用—— Homebrew
    Mac os系统gdb调试器的安装与使用
    项目里如何访问AppDelegate
    leetcode 141. Linked List Cycle
    leetcode 206. Reverse Linked List
    从阿里巴巴面试题到java类加载机制
    SpringBoot处理日期转换问题
  • 原文地址:https://www.cnblogs.com/chenxiaoran666/p/CF1303G.html
Copyright © 2011-2022 走看看