uoj#397. 【NOI2018】情報中心
阿新 • • 發佈:2019-01-20
題意
給出一棵個點的樹,每條邊有一個非負邊權。再給出樹上的條鏈,鏈有費用。求選出兩條邊相交的鏈,它們的並的邊權和減去費用之和的最大值。
多組資料,。背景
我好菜啊 我好菜啊 我套路都不會 我暴力都寫掛
在賽場上獲得了和自己水平很相配的5分。題解
開始照著題解念
將一條鏈的邊權和記為 ,費用記為 ,點的帶權深度記為 。
我們分類討論一波。
當兩條鏈的 不同(記為 和 )時,答案是兩條鏈的價值去掉中間的交,也即 。
我們把每條鏈拆成兩條由 和一個端點組成的鏈。對於每個點 ,維護 表示下端點在 的子樹裡,上端點不帶權深度為 的鏈的 的最大值。這個東西線段樹合併上去就可以。
對於線段樹上的一個節點,用其左子節點的+右子節點的 (再減去當前列舉的點的 )更新答案。在合併時,因為要求兩條鏈分屬當前點不同子節點的子樹,需要用被合併的兩個節點的左/右子節點交叉更新答案。//說不明白 程式碼裡會涉及到
當兩條鏈的 相同時,答案可以記為 。
我們把一條鏈的總價值即 掛到另一個端點上,列舉分叉的位置,對於一個端點在它不同子節點的子樹中的鏈的另一個端點求最遠點對即為答案。因為邊權非負/*之前問jcy 鏈的總價值不是非負的 會不會出問題 得到解答後我認為自己非常sb 然後jcy對我的觀點表示贊同*/,兩個集合的最遠點對資訊可合併。所以我們實現的時候先枚端點到 的倒數第二個節點 ,對於所有鏈的端點建虛樹,然後自下至上合併。
總複雜度。程式碼
#include<bits/stdc++.h>
#define N 50004
#define V 1800000
#define mid (l+r>>1)
#define il inline
#define re return
#define mx(xx,yy) xx<yy?xx=yy:0
using namespace std;
typedef long long ll;
const ll inf=-1e17;
int T,n,m,D,f[N],dep[N],lg[N*2],to[N],hd[N],lk[N],
fa[18][N*2],siz[N],son[N],top[N],dfn[N],cnt,st[N],tot,
rt[N],c[V][2];
ll Dep[N],ans,tmx,tnow,add,mx0[V],mx1[V],tmp,ww;
struct sig{int u;ll w;}t1,t2;
struct dat{int u;sig v;};
vector<dat>s2[N];
il int lca(int u,int v){
if(u==v)re u;
if(dfn[u]>dfn[v])swap(u,v);
register int d=lg[dfn[v]-dfn[u]];
return min(fa[d][dfn[u]],fa[d][dfn[v]-(1<<d)+1]);
}
il ll dis(int u,int v){re u&&v?Dep[u]+Dep[v]-2*Dep[lca(u,v)]:inf;}
il ll dis(sig u,sig v){re dis(u.u,v.u)+u.w+v.w;}
il void upd(sig u,sig v){(tnow=dis(u,v))>tmx?t1=u,t2=v,tmx=tnow:0;}
struct mxd{
sig a,b;int no;
il void ini(){a.u=b.u=0;}
il void uni(mxd &c){
if(!a.u)re a=c.a,b=c.b,c.ini();
t1=a,t2=c.a,tmx=dis(a,t2);
upd(a,c.b),upd(b,c.a),upd(b,c.b);
mx(ans,(tmx>>1)-Dep[no]);
upd(a,b),upd(c.a,c.b);
a=t1,b=t2,c.ini();
}
}d[N],ad;
il int nxt(int u,int v){
for