DTOJ #4735. Sum of Prefix Sums

题解

考虑两条路径 a 1 , a 2 , … , a n a_1,a_2,\dots,a_n a1,a2,,an b 1 , b 2 , … , b m b_1,b_2,\dots,b_m b1,b2,,bm 如何合并,不妨设方向为 a 1 → b m a_1 \to b_m a1bm,那么显然 b 1 → b m b_1 \to b_m b1bm 部分的答案不变,记为 s u m b sum_b sumb a 1 → a n a_1 \to a_n a1an 的答案则需要考虑 { b m } \{b_m\} {bm} 的影响,即从 ∑ i = 1 n ( n − i + 1 ) × a i \sum_{i=1}^n (n-i+1) \times a_i i=1n(ni+1)×ai 变为 ∑ i = 1 n ( n − i + 1 ) × a i + m × a i \sum_{i=1}^n (n-i+1) \times a_i+m \times a_i i=1n(ni+1)×ai+m×ai ,记 ∑ a i = s a \sum a_i=s_a ai=sa,则合并后路径的答案为 s u m b + s u m a + m × s a sum_b+sum_a+m \times s_a sumb+suma+m×sa,所以我们考虑点分治,用李超线段树求出 ( m , s u m b ) (m,sum_b) (m,sumb) 时的最大值即可。

注意路径有顺序,还要反着做一遍。

代码

#include<bits/stdc++.h>
#define LL long long
using namespace std;
inline int read(){
	int k=0,f=1;char c=getchar();
	while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
	while(c>='0'&&c<='9')k=k*10+c-'0',c=getchar();
	return k*f;
}
const int N=1e5+10;
const LL MAXN=1e12;
int n,a[N];
int tot,ver[N<<1],fst[N],nxt[N<<1];
inline void add(int x,int y){ver[++tot]=y;nxt[tot]=fst[x];fst[x]=tot;}
struct Node{
	LL k,b;
}t[N<<6];
inline double F(Node f,double x){return 1.0*f.k*x+f.b;}
int lc[N<<6],rc[N<<6],vt[N<<6],now,root;
int sz[N],v[N],rsy,rt,mn;
queue<int> q;
int lin[N],lt;
LL ans;
void change(int &p,LL l,LL r,Node x){
	if(!p)p=++now;
	if(!vt[p]){
		vt[p]=1;
		t[p]=x;
		return;
	}
	LL l1=F(t[p],l),l2=F(x,l);
	LL r1=F(t[p],r),r2=F(x,r);
	if(l2>l1&&r2>r1){
		t[p]=x;
		return;
	}
	if(l2<l1&&r2<r1)return;
	if(l==r)return;
	LL mid=(l+r)>>1;
	if(F(t[p],1.0*(l+r)/2)<F(x,1.0*(l+r)/2))swap(t[p],x);
	if(t[p].k>x.k)change(lc[p],l,mid,x);
	else change(rc[p],mid+1,r,x);
}
LL query(int p,LL l,LL r,LL x){
	if(!vt[p])return 0;
	LL s1=F(t[p],x),s2;
	LL mid=(l+r)>>1;
	if(x<=mid)s2=query(lc[p],l,mid,x);
	else s2=query(rc[p],mid+1,r,x);
	return max(s1,s2);
}
void dfs_pre(int x,int fa){
	rsy++;
	for(int i=fst[x];i;i=nxt[i]){
		int y=ver[i];if(v[y]||y==fa)continue;
		dfs_pre(y,x);
	}
}
void dfs_get(int x,int fa){
	sz[x]=1;int s=0;
	for(int i=fst[x];i;i=nxt[i]){
		int y=ver[i];if(y==fa||v[y])continue;
		dfs_get(y,x);
		sz[x]+=sz[y];
		s=max(s,sz[y]);
	}
	s=max(s,rsy-sz[x]);
	if(s<mn)mn=s,rt=x;
}
void dfs_s(int x,int fa,int dep,LL s1,LL s2,LL s){
	ans=max(ans,s1);ans=max(ans,s2);
	for(int i=fst[x];i;i=nxt[i]){
		int y=ver[i];if(y==fa||v[y])continue;
		dfs_s(y,x,dep+1,s1+1ll*a[y]*(dep+1),s2+s+a[y],s+a[y]);
	}
}
void dfs_ans(int x,int fa,int dep,LL s1,LL s){
//	cout<<"dfs_ans "<<x<<" "<<dep<<" "<<s1<<" "<<s<<" "<<query(1,1,MAXN,s)<<endl;
	ans=max(ans,query(1,1,MAXN,s)+s1);
	for(int i=fst[x];i;i=nxt[i]){
		int y=ver[i];if(y==fa||v[y])continue;
		dfs_ans(y,x,dep+1,1ll*a[y]*(dep+1)+s1,s+a[y]);
	}
}
void dfs_jia(int x,int fa,int dep,LL s2,LL s){
	change(root,1,MAXN,(Node){dep,s2});
	for(int i=fst[x];i;i=nxt[i]){
		int y=ver[i];if(y==fa||v[y])continue;
		dfs_jia(y,x,dep+1,s2+s+a[y],s+a[y]);
	}
}
void cl(int p,LL l,LL r){
	t[p]=(Node){0,0};vt[p]=0;
	if(l==r)return;
	LL mid=(l+r)>>1;
	if(lc[p])cl(lc[p],l,mid);
	if(rc[p])cl(rc[p],mid+1,r);
	lc[p]=rc[p]=0;
}
int summ=0;
inline void prework(){
	q.push(1);
	while(q.size()){
		int x=q.front();q.pop();
		rsy=rt=0;mn=1e9;
		dfs_pre(x,x);dfs_get(x,x);
		v[rt]=1;lt=0;
	//	cout<<"Rt "<<rt<<" "<<ans<<endl;
		dfs_s(rt,rt,1,a[rt],a[rt],a[rt]);
		for(int i=fst[rt];i;i=nxt[i]){
			int y=ver[i];if(v[y])continue;
			lin[++lt]=y;
//			cout<<"Lin "<<rt<<" "<<y<<endl;
		}
		for(int i=1;i<=lt;++i){
			int y=lin[i];
			dfs_ans(y,rt,1,a[y],a[y]);dfs_jia(y,rt,2,a[rt]*2+a[y],a[rt]+a[y]);
		}
		cl(1,1,MAXN);now=0;root=0;
		for(int i=lt;i;--i){
			int y=lin[i];
			dfs_ans(y,rt,1,a[y],a[y]);dfs_jia(y,rt,2,a[rt]*2+a[y],a[rt]+a[y]);
		}
		cl(1,1,MAXN);now=0;root=0;
		for(int i=1;i<=lt;++i)q.push(lin[i]);
	}
	printf("%lld\n",ans);
}
int main(){
//	freopen("sum1.in","r",stdin);
	n=read();
	for(int i=1;i<n;++i){
		int x=read(),y=read();
		add(x,y);add(y,x);
	}
	for(int i=1;i<=n;++i)a[i]=read();
	prework();
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值