原文链接www.cnblogs.com/zhouzhendong/p/UOJ397.com

前言

这真可做吗?只能贺题解啊……

题解

我们称一条路径的 LCA 为这条路径两端点的 LCA。

我们将相交的路径分成两种:

  1. 两条路径的 LCA 相同。
  2. 两条路径的 LCA 不同。

设路径 \(1\) 的两端点为 \(x_1,y_1\),LCA 为 \(lca_1\) ,消耗为 \(v_1\) 。

设路径 \(2\) 的两端点为 \(x_2,y_2\),LCA 为 \(lca_2\) ,消耗为 \(v_2\) 。

设原树上两点带权距离为 \(Dis(x,y)\),一个点的带权深度为 \(len_x\) 。

接下来我们分两种情况讨论一下这个问题。

\(lca_1 \neq lca_2\)

\[ans = Dis(x_1,y_1) + Dis(x_2,y_2) - v_1 - v_2 - len[LCA(x_1,x_2)] + \max(len(lca_1,lca_2)
\]

大力线段树合并即可。

\(lca_1 = lca _2 = 1\)

\[ans \times 2 = -2 v_1-2v_2 + Dis(x_1,y_1) + Dis(x_2,y_2) + len[x_1] + len [x_2] + Dis(y_1,y_2) - 2 len[p]
\]

类似于WC2018通道 的做法,我们修改 \(y_1,y_2\) 的深度定义,然后对 \(p\) 进行 dfs,对 \(y_1,y_2\) 维护最远点对即可。

\(lca_1 = lca_2\)

如果 LCA 不恒为 1 ,那么我们只需要枚举 LCA,然后每次建个虚树实现即可。

以上总时间复杂度为 \(O((n+m) \log n)\) 。

代码

#include <bits/stdc++.h>
#define clr(x) memset(x,0,sizeof x)
#define For(i,a,b) for (int i=(a);i<=(b);i++)
#define Fod(i,b,a) for (int i=(b);i>=(a);i--)
#define fi first
#define se second
#define pb(x) push_back(x)
#define mp(x,y) make_pair(x,y)
#define outval(x) cerr<<#x" = "<<x<<endl
#define outtag(x) cerr<<"---------------"#x"---------------"<<endl
#define outarr(a,L,R) cerr<<#a"["<<L<<".."<<R<<"] = ";\
For(_x,L,R)cerr<<a[_x]<<" ";cerr<<endl;
using namespace std;
typedef long long LL;
LL read(){
LL x=0,f=0;
char ch=getchar();
while (!isdigit(ch))
f|=ch=='-',ch=getchar();
while (isdigit(ch))
x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
return f?-x:x;
}
const int N=50005,M=100005;
const LL INF=1e17;
int T,n,m;
struct Graph{
int cnt,y[M],z[M],nxt[M],fst[N];
void clear(int n){
cnt=1,memset(fst,0,(n+5)<<2);
}
void add(int a,int b,int c){
y[++cnt]=b,nxt[cnt]=fst[a],fst[a]=cnt,z[cnt]=c;
}
}g;
int depth[N],fa[N][20];
int I[N],O[N],Time;
LL len[N];
void dfs(int x,int pre,int D,LL L){
I[x]=++Time;
depth[x]=D,len[x]=L,fa[x][0]=pre;
For(i,1,19)
fa[x][i]=fa[fa[x][i-1]][i-1];
for (int i=g.fst[x];i;i=g.nxt[i]){
int y=g.y[i];
if (y!=pre)
dfs(y,x,D+1,L+g.z[i]);
}
O[x]=Time;
}
int LCA(int x,int y){
if (depth[x]<depth[y])
swap(x,y);
Fod(i,19,0)
if (depth[x]-(1<<i)>=depth[y])
x=fa[x][i];
if (x==y)
return x;
Fod(i,19,0)
if (fa[x][i]!=fa[y][i])
x=fa[x][i],y=fa[y][i];
return fa[x][0];
}
LL Dis(int x,int y){
return len[x]+len[y]-len[LCA(x,y)]*2;
}
LL ans;
struct ch{
int x,y,lca;
LL v,co;
}a[M];
namespace S1{
const int S=M*20*5;
int ls[S],rs[S];
LL mxL[S],mxR[S];
int cnt;
void pushup(int rt){
mxL[rt]=max(mxL[ls[rt]],mxL[rs[rt]]);
mxR[rt]=max(mxR[ls[rt]],mxR[rs[rt]]);
}
void Ins(int &rt,int L,int R,int x,LL vL,LL vR){
if (!rt)
rt=++cnt,ls[rt]=rs[rt]=0,mxL[rt]=mxR[rt]=-INF;
mxL[rt]=max(mxL[rt],vL);
mxR[rt]=max(mxR[rt],vR);
if (L==R)
return;
int mid=(L+R)>>1;
if (x<=mid)
Ins(ls[rt],L,mid,x,vL,vR);
else
Ins(rs[rt],mid+1,R,x,vL,vR);
}
int Del(int rt,int L,int R,int x){
if (!rt)
return 0;
int now=++cnt;
ls[now]=ls[rt],rs[now]=rs[rt];
mxL[now]=mxR[now]=-INF;
if (L==R)
return now;
int mid=(L+R)>>1;
if (x<=mid)
ls[now]=Del(ls[rt],L,mid,x);
else
rs[now]=Del(rs[rt],mid+1,R,x);
pushup(now);
return now;
}
LL Add;
int Merge(int x,int y,int L,int R){
if (!x||!y)
return x|y;
int rt=++cnt;
ls[rt]=rs[rt]=0,mxL[rt]=mxR[rt]=-INF;
if (L==R){
mxL[rt]=max(mxL[x],mxL[y]);
mxR[rt]=max(mxR[x],mxR[y]);
return rt;
}
int mid=(L+R)>>1;
ans=max(ans,Add+max(mxL[ls[x]]+mxR[rs[y]],mxL[ls[y]]+mxR[rs[x]]));
ls[rt]=Merge(ls[x],ls[y],L,mid);
rs[rt]=Merge(rs[x],rs[y],mid+1,R);
pushup(rt);
return rt;
}
vector <int> id[N];
int rt[N];
void dfs(int x,int pre){
for (int i=g.fst[x];i;i=g.nxt[i]){
int y=g.y[i];
if (y!=pre)
dfs(y,x);
}
Add=-len[x];
for (int i : id[x]){
int tmp=0;
Ins(tmp,0,n,depth[a[i].lca],a[i].v,a[i].v+len[a[i].lca]);
rt[x]=Merge(rt[x],tmp,0,n);
}
for (int i=g.fst[x];i;i=g.nxt[i]){
int y=g.y[i];
if (y!=pre){
rt[y]=Del(rt[y],0,n,depth[x]);
rt[x]=Merge(rt[x],rt[y],0,n);
}
}
}
void Solve(){
For(i,1,n)
id[i].clear(),rt[i]=0;
For(i,1,m){
if (a[i].x!=a[i].lca)
id[a[i].x].pb(i);
if (a[i].y!=a[i].lca)
id[a[i].y].pb(i);
}
cnt=0;
mxL[0]=mxR[0]=-INF;
dfs(1,0);
}
}
namespace S2{
LL res;
vector <int> id[N];
int st[N],top;
int vid[M*2],ac;
bool cmpI(int a,int b){
return I[a]<I[b];
}
struct Node{
int x;
LL v;
Node(){}
Node(int _x,LL _v){
x=_x,v=_v;
}
};
vector <Node> vn[N];
typedef pair <Node,Node> PC;
PC pr[N];
LL dis(Node a,Node b){
if (!a.x&&!b.x)
return -(INF<<4);
if (!a.x||!b.x)
return -(INF<<2);
return Dis(a.x,b.x)+a.v+b.v;
}
PC Merge(PC a,PC b,int f,LL Add){
LL v00=dis(a.fi,b.fi);
LL v01=dis(a.fi,b.se);
LL v10=dis(a.se,b.fi);
LL v11=dis(a.se,b.se);
if (f)
res=max(res,max(max(v00,v01),max(v10,v11))+Add);
LL va=dis(a.fi,a.se);
LL vb=dis(b.fi,b.se);
LL mx=max(max(max(v00,v01),max(v10,v11)),max(va,vb));
if (mx==v00)
return mp(a.fi,b.fi);
if (mx==v01)
return mp(a.fi,b.se);
if (mx==v10)
return mp(a.se,b.fi);
if (mx==v11)
return mp(a.se,b.se);
if (mx==va)
return a;
if (mx==vb)
return b;
}
void Solve(int x){
ac=0;
for (int i : id[x])
if (a[i].x!=a[i].y)
vid[++ac]=a[i].x,vid[++ac]=a[i].y;
vid[++ac]=x;
sort(vid+1,vid+ac+1,cmpI);
ac=unique(vid+1,vid+ac+1)-vid-1;
For(i,1,ac)
vn[vid[i]].clear();
for (int i : id[x])
if (a[i].x!=a[i].y){
vn[a[i].x].pb(Node(a[i].y,a[i].v-a[i].co+len[a[i].x]));
vn[a[i].y].pb(Node(a[i].x,a[i].v-a[i].co+len[a[i].y]));
}
top=0;
assert(vid[1]==x);
For(_,1,ac){
int i=vid[_];
pr[i]=mp(Node(0,0),Node(0,0));
while (!vn[i].empty()){
pr[i]=Merge(pr[i],mp(vn[i].back(),Node(0,0)),i!=x,-len[i]*2);
vn[i].pop_back();
}
if (top){
int lca=LCA(i,st[top]);
while (depth[st[top]]>depth[lca]){
int id=st[top];
if (depth[st[top-1]]>=depth[lca])
pr[st[top-1]]=Merge(pr[st[top-1]],pr[id],st[top-1]!=x,-len[st[top-1]]*2),top--;
else
pr[lca]=pr[id],st[top]=lca;
}
}
st[++top]=i;
}
while (top>1){
int id=st[top];
pr[st[top-1]]=Merge(pr[st[top-1]],pr[id],st[top-1]!=x,-len[st[top-1]]*2);
top--;
}
}
void dfs(int x,int pre){
for (int i=g.fst[x];i;i=g.nxt[i]){
int y=g.y[i];
if (y!=pre)
dfs(y,x);
}
Solve(x);
}
void Solve(){
For(i,1,n)
id[i].clear();
For(i,1,m)
id[a[i].lca].pb(i);
res=-INF*2;
dfs(1,0);
ans=max(ans,res/2);
}
}
void Solve(){
n=read();
g.clear(n);
For(i,1,n-1){
int a=read(),b=read(),c=read();
g.add(a,b,c),g.add(b,a,c);
}
Time=0;
dfs(1,0,0,0);
m=read();
For(i,1,m){
a[i].x=read(),a[i].y=read(),a[i].co=read();
a[i].v=Dis(a[i].x,a[i].y)-a[i].co;
a[i].lca=LCA(a[i].x,a[i].y);
}
ans=-INF/2;
S1::Solve();
S2::Solve();
if (ans==-INF/2)
puts("F");
else
printf("%lld\n",ans);
}
int main(){
#ifdef zzd
freopen("x.in","r",stdin);
#endif
T=read();
while (T--)
Solve();
return 0;
}

UOJ#397. 【NOI2018】情报中心 线段树合并 虚树的更多相关文章

  1. BZOJ5419[Noi2018]情报中心——线段树合并+虚树+树形DP

    题目链接: [NOI2018]情报中心 题目大意:给出一棵n个节点的树,边有非负边权,并给出m条链,对于每条链有一个代价,要求选出两条有公共边的链使两条链的并的边权和-两条链的代价和最大. 花了一天的 ...

  2. luogu P4775 [NOI2018]情报中心 线段树合并 虚树 树的直径trick

    LINK:情报中心 神题! 写了一下午 写到肚子疼. 调了一晚上 调到ex 用的是网上dalao的方法 跑的挺快的. 对于链的暴力 我不太会kk. 直接说正解吧: 分类讨论两种情况: 1 答案的两条链 ...

  3. Codeforces 1276F - Asterisk Substrings(SAM+线段树合并+虚树)

    Codeforces 题面传送门 & 洛谷题面传送门 SAM hot tea %%%%%%% 首先我们显然可以将所有能够得到的字符串分成六类:\(\varnothing,\text{*},s, ...

  4. 2016湖南省赛 I Tree Intersection(线段树合并,树链剖分)

    2016湖南省赛 I Tree Intersection(线段树合并,树链剖分) 传送门:https://ac.nowcoder.com/acm/contest/1112/I 题意: 给你一个n个结点 ...

  5. 【BZOJ5329】【SDOI2018】战略游戏(圆方树,虚树)

    [BZOJ5329][SDOI2018]战略游戏(圆方树,虚树) 题面 BZOJ 洛谷 Description 省选临近,放飞自我的小Q无心刷题,于是怂恿小C和他一起颓废,玩起了一款战略游戏. 这款战 ...

  6. bzoj5315/luoguP4517 [SDOI2018]战略游戏(圆方树,虚树)

    bzoj5315/luoguP4517 [SDOI2018]战略游戏(圆方树,虚树) bzoj Luogu 题目描述略(太长了) 题解时间 切掉一个点,连通性变化. 上圆方树. $ \sum |S| ...

  7. CF666E Forensic Examination SAM+线段树合并+前缀树倍增

    $ \color{#0066ff}{ 题目描述 }$ 给你一个串\(S\)以及一个字符串数组\(T[1..m]\),\(q\)次询问,每次问\(S\)的子串\(S[p_l..p_r]\)在\(T[l. ...

  8. [POI2011]ROT-Tree Rotations 线段树合并|主席树 / 逆序对

    题目[POI2011]ROT-Tree Rotations [Description] 现在有一棵二叉树,所有非叶子节点都有两个孩子.在每个叶子节点上有一个权值(有\(n\)个叶子节点,满足这些权值为 ...

  9. 【模板】【P3605】【USACO17JAN】Promotion Counting 晋升者计数——动态开点和线段树合并(树状数组/主席树)

    (题面来自Luogu) 题目描述 奶牛们又一次试图创建一家创业公司,还是没有从过去的经验中吸取教训--牛是可怕的管理者! 为了方便,把奶牛从 1⋯N(1≤N≤100,000) 编号,把公司组织成一棵树 ...

随机推荐

  1. PDF时间戳 服务器

    好用权威免费的PDF文件数字签名时间戳服务器URL http://tss.pki.gva.es:8318/tsa

  2. Mycat分布式数据库架构解决方案--Mycat的介绍

    echo编辑整理,欢迎转载,转载请声明文章来源.欢迎添加echo微信(微信号:t2421499075)交流学习. 百战不败,依不自称常胜,百败不颓,依能奋力前行.--这才是真正的堪称强大!!! 如果我 ...

  3. jQuery动画(带参数)

    <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title> ...

  4. jQuery 名称发生冲突怎么办【问题】

    [问题]jQuery 名称发生冲突怎么办? [答案]jQuery 使用 $ 符号作为 jQuery 的简介方式.某些其他 JavaScript 库中的函数(比如 Prototype)同样使用 $ 符号 ...

  5. React Native 开发豆瓣评分(三)集成 Redux

    什么是 redux redux 是一个用于管理 js 应用状态(state)的容器.比如组件 A 发生了变化,组件 B 要同时做出响应.常见的应用场景就是用户的登录退出操作:未登录状态,个人中心显示登 ...

  6. RobotFramework+Eclipse的安装和配置(一)

    最近想学robotframwork来做自动化,那立马就来开始上手 想动手,起码要先下载工具,工具及框架 工具介绍 Robotframework:一款自动化测试框架. Eclipse:一款编辑工具,可以 ...

  7. p4.BTC-实现

    比特币是基于 transaction-based ledger.(隐私保护性很好,但是在转账中需要说明币的来源,比较麻烦) 比特币的全节点需要维护一个UTXO的数据结构(unspent transac ...

  8. Django使用swagger生成接口文档

    参考博客:Django接入Swagger,生成Swagger接口文档-操作解析 Swagger是一个规范和完整的框架,用于生成.描述.调用和可视化RESTful风格的Web服务.总体目标是使客户端和文 ...

  9. 华为云PaaS首席科学家:Cloud Native +AI,企业数字化转型的最佳拍档

    近日,在2019华为全球分析师大会期间,华为云PaaS首席科学家熊英博士在+智能,见未来(华为云&大数据)的分论坛上,从云计算行业发展谈起,深入云原生发展趋势,对华为云智能应用平台做了深度解读 ...

  10. yolov3---报"段错误"的可能原因及解决方法

    参考: ************************************************************************************************ ...