Gym - 101002K:YATP (树分治+二分+斜率优化)
题意:给定带点权边权的树,定义路径的花费=路径边权和e+起点点权w[s]*终点点权w[t]。N<2e5,e,w<1e6;
思路:首先,需要树分治。 然后得到方程dp[i]=min{ dis[i]+dis[j]+w[i]*w[j] },很显然需要斜率优化。
注意维护凸包的时候是需要保证w[j]是单调的,这样才能用不等式维护队尾。 由于w[i]不是对应的队尾,所以我们还要二分凸包。
还有个问题,怎么确定我们得到的i和j不是在同一个子树呢? 因为如果在一颗子树的时候dp[i]=dis[i]+dis[j]+w[i]*w[j]-2*dis[LCA]。 其实没必要考虑这个问题,因为当LCA为根的时候会更新答案。(这一点想不到估计要很难去维护了)
#include<bits/stdc++.h> #define ll long long #define rep(i,a,b) for(int i=a;i<=b;i++) using namespace std; const int maxn=400010; int Laxt[maxn],Next[maxn],To[maxn],Len[maxn],cnt; int sz[maxn],son[maxn],rt,all,vis[maxn],S[maxn],tot; ll ans[maxn],a[maxn],dis[maxn],sum; int q[maxn],top; bool cmp(int x,int y) { int xx=x,yy=y; if(a[xx]==a[yy]) return dis[xx]<dis[yy]; return a[xx]<a[yy]; } ll getans(int p,int k) { return dis[k]+dis[p]+a[p]*a[k]; } void add(int u,int v,int w) { Next[++cnt]=Laxt[u]; Laxt[u]=cnt; To[cnt]=v; Len[cnt]=w; } void dfs1(int u,int f) { sz[u]=1; son[u]=0; for(int i=Laxt[u];i;i=Next[i]){ if(To[i]!=f&&!vis[To[i]]) { dfs1(To[i],u); sz[u]+=sz[To[i]]; son[u]=max(son[u],sz[To[i]]); } } son[u]=max(son[u],all-son[u]); if(son[u]<son[rt]) rt=u; } void cal(int p) { if(top==0) return ;int L=1,R=top-1,Mid; ans[p]=min(ans[p],getans(p,q[top])); while(L<=R){ Mid=(L+R)>>1; ll tmp1=getans(p,q[Mid]),tmp2=getans(p,q[Mid+1]); if(tmp1<tmp2) R=Mid-1,ans[p]=min(ans[p],tmp1); else L=Mid+1,ans[p]=min(ans[p],tmp2); } } void get(int u,int f) { cal(u); for(int i=Laxt[u];i;i=Next[i]) if(To[i]!=f&&!vis[To[i]]) get(To[i],u); } bool check(int p){ return (dis[p]-dis[q[top]])*(a[p]-a[q[top-1]])<= (dis[p]-dis[q[top-1]])*(a[p]-a[q[top]]); } void ADD(int p) { if(top&&a[p]==a[q[top]]&&dis[p]<dis[q[top]]) top--; while(top>1&&check(p)) top--; q[++top]=p; } void get(int u,int f,ll D) { dis[u]=D; sz[u]=1; S[++tot]=u; for(int i=Laxt[u];i;i=Next[i]) if(To[i]!=f&&!vis[To[i]]){ get(To[i],u,D+Len[i]); sz[u]+=sz[To[i]]; } } void solve(int u,int f) { vis[u]=1; dis[u]=0; top=0; tot=0; S[++tot]=u; for(int i=Laxt[u];i;i=Next[i]){ if(!vis[To[i]]&&To[i]!=f) get(To[i],u,Len[i]); } sort(S+1,S+tot+1,cmp); rep(i,1,tot) ADD(S[i]); rep(i,1,tot) cal(S[i]); for(int i=Laxt[u];i;i=Next[i]){ int v=To[i]; if(!vis[v]&&v!=f) { all=sz[v]; rt=0; dfs1(v,0); solve(rt,0); } } } int main() { int N,u,v,w; scanf("%d",&N); son[0]=N+1; rep(i,1,N) scanf("%lld",&a[i]); rep(i,1,N) ans[i]=a[i]*a[i]; rep(i,1,N-1){ scanf("%d%d%d",&u,&v,&w); add(u,v,w); add(v,u,w); } all=N; rt=0; dfs1(1,0); solve(rt,0); rep(i,1,N) sum+=ans[i]; printf("%lld\n",sum); return 0; }
It is your time to fight!