题意:给定两棵树T1,T2,求d1[x] + d1[y] - d1[lca1(x, y)] - d2[lca2(x, y)]的最大值。

解:考虑把上面这个毒瘤东西化一下。发现它就是T1中x,y到根的路径并 - T2中x,y到根的路径交。

也就是T1中的一个三叉状路径和T2中lca到根的路径。

根据情报中心的套路,把这个东西 * 2,发现就是d1[x] + d1[y] + dis1(x, y) - d2[x] - d2[y] + dis2(x, y)

令val[i] = d1[i] - d2[i],我们就要令val[x] + val[y] + dis1(x, y) + dis2(x, y)最大。

考虑在T1上边分治,这样的话dis1(x, y) = d[x] + d[y] + e。e是分治边的长度。

在T2上把T1这次分治的点全部提出来建虚树。我们不妨把每个T1的节点挂到对应的虚树节点上,边权为val[x] + d[x],那么我们就是要在虚树上挂的点中找到两个异色节点,使得它们距离最远。

此时我们有一个非常优秀的O(n)DFS算法......但是我就是SB中的SB中的SB,把这个问题搞成了nlogn。这个解决之后本题就做完了。为了卡常用了优秀的zkw线段树。

 #include <bits/stdc++.h>

 #define forson(x, i) for(register int i = e[x]; i; i = edge[i].nex)

 #define add(x, y, z) edge[++tp].v = y; edge[tp].len = z; edge[tp].nex = e[x]; e[x] = tp
#define ADD(x, y) EDGE[++TP].v = y; EDGE[TP].nex = E[x]; E[x] = TP typedef long long LL;
const int N = ;
const LL INF = 4e18; inline char gc() {
static char buf[N], *p1(buf), *p2(buf);
if(p1 == p2) p2 = (p1 = buf) + fread(buf, , N, stdin);
return p1 == p2 ? EOF : *p1++;
} template <class T> inline void read(T &x) {
x = ;
register char c(gc());
bool f(false);
while(c < '' || c > '') {
if(c == '-') f = true;
c = gc();
}
while(c >= '' && c <= '') {
x = x * + c - ;
c = gc();
}
if(f) x = (~x) + ;
return;
} struct Edge {
int nex, v;
bool vis;
LL len;
}edge[N << ], EDGE[N << ]; int tp = , TP; int pw[N << ], n, e[N], Cnt, _n, small, root, imp[N], K, use[N], Time, stk[N], top, E[N], RT, POS[N], NUM, SIZ[N], ID[N], vis[N], siz[N];
LL val[N], ans = -INF, d[N], VAL[N], H[N], C[N], W[N];
std::vector<int> G[N];
std::vector<LL> Len[N]; namespace seg2 {
LL large[N << ];
void build(int l, int r, int o) {
if(l == r) {
large[o] = VAL[ID[r]];
//if(F) printf("p = %d x = %d val = %lld \n", r, ID[r], VAL[ID[r]]);
return;
}
int mid((l + r) >> );
build(l, mid, o << );
build(mid + , r, o << | );
large[o] = std::max(large[o << ], large[o << | ]);
return;
}
LL getMax(int L, int R, int l, int r, int o) {
if(L <= l && r <= R) {
return large[o];
}
LL ans = -INF;
int mid((l + r) >> );
if(L <= mid) ans = getMax(L, R, l, mid, o << );
if(mid < R) ans = std::max(ans, getMax(L, R, mid + , r, o << | ));
return ans;
}
} namespace seg {
LL large[N << ];
int lm;
inline void build(int n) {
lm = ; n += ;
while(lm < n) lm <<= ;
large[lm] = -INF;
for(register int i = ; i <= n; i++) {
large[i + lm] = VAL[ID[i]];
}
for(register int i = n + ; i < lm; i++) {
large[i + lm] = -INF;
}
for(int i = lm - ; i >= ; i--) {
large[i] = std::max(large[i << ], large[i << | ]);
}
return;
}
inline LL getMax(int L, int R) {
LL ans = -INF;
for(int s = lm + L - , t = lm + R + ; s ^ t ^ ; s >>= , t >>= ) {
if(t & ) {
ans = std::max(ans, large[t ^ ]);
}
if((s & ) == ) {
ans = std::max(ans, large[s ^ ]);
}
}
return ans;
}
} namespace t1 {
int e[N], pos2[N], ST[N << ][], num2, deep[N];
LL d[N];
Edge edge[N << ]; int tp;
void DFS_1(int x, int f) {
deep[x] = deep[f] + ;
pos2[x] = ++num2;
ST[num2][] = x;
forson(x, i) {
int y(edge[i].v);
if(y == f) continue;
d[y] = d[x] + edge[i].len;
DFS_1(y, x);
ST[++num2][] = x;
}
return;
}
inline void prework() {
for(register int j(); j <= pw[num2]; j++) {
for(register int i(); i + ( << j) - <= num2; i++) {
if(deep[ST[i][j - ]] < deep[ST[i + ( << (j - ))][j - ]]) {
ST[i][j] = ST[i][j - ];
}
else {
ST[i][j] = ST[i + ( << (j - ))][j - ];
}
}
}
return;
}
inline int lca(int x, int y) {
x = pos2[x], y = pos2[y];
if(x > y) std::swap(x, y);
int t(pw[y - x + ]);
if(deep[ST[x][t]] < deep[ST[y - ( << t) + ][t]]) {
return ST[x][t];
}
else {
return ST[y - ( << t) + ][t];
}
}
inline LL dis(int x, int y) {
return d[x] + d[y] - * d[lca(x, y)];
}
} void rebuild(int x, int f) {
int temp();
for(register int i(); i < G[x].size(); i++) {
int y = G[x][i];
LL len = Len[x][i];
if(y == f) continue;
if(!temp) {
add(x, y, len);
add(y, x, len);
temp = x;
}
else if(i == G[x].size() - ) {
add(temp, y, len);
add(y, temp, len);
}
else {
add(temp, ++Cnt, );
add(Cnt, temp, );
temp = Cnt;
add(temp, y, len);
add(y, temp, len);
}
d[y] = d[x] + len;
rebuild(y, x);
}
return;
} void getroot(int x, int f) {
siz[x] = ;
//printf("getroot : x = %d \n", x);
forson(x, i) {
int y(edge[i].v);
if(y == f || edge[i].vis) continue;
getroot(y, x);
if(small > std::max(siz[y], _n - siz[y])) {
small = std::max(siz[y], _n - siz[y]);
root = i;
}
siz[x] += siz[y];
}
//printf("siz %d = %d \n", x, siz[x]);
return;
} void DFS_1(int x, int f, int flag) {
siz[x] = ;
//printf("x = %d d = %lld \n", x, d[x]);
if(!flag && x <= n) {
imp[++K] = x;
vis[x] = Time;
//if(F) printf("vis %d = Time \n", x);
}
else if(flag && x <= n) {
imp[++K] = x;
}
forson(x, i) {
int y(edge[i].v);
if(y == f || edge[i].vis) continue;
d[y] = d[x] + edge[i].len;
DFS_1(y, x, flag);
siz[x] += siz[y];
}
return;
} inline void work(int x) {
if(use[x] == Time) return;
use[x] = Time;
E[x] = ;
return;
} inline bool cmp(const int &a, const int &b) {
return t1::pos2[a] < t1::pos2[b];
} inline void build_t() {
std::sort(imp + , imp + K + , cmp);
K = std::unique(imp + , imp + K + ) - imp - ;
work(imp[]);
stk[top = ] = imp[];
for(register int i(); i <= K; i++) {
int x = imp[i], y = t1::lca(x, stk[top]);
work(x); work(y);
while(top > && t1::pos2[y] <= t1::pos2[stk[top - ]]) {
ADD(stk[top - ], stk[top]);
top--;
}
if(y != stk[top]) {
ADD(y, stk[top]);
stk[top] = y;
}
stk[++top] = x;
}
while(top > ) {
ADD(stk[top - ], stk[top]);
top--;
}
RT = stk[top];
return;
} void dfs_2(int x) {
//printf("dfs_2 x = %d \n", x);
SIZ[x] = ;
POS[x] = ++NUM;
ID[NUM] = x;
if(vis[x] == Time) VAL[x] = t1::d[x] + val[x] + d[x];
else VAL[x] = -INF;
/*if(F && x == 3) {
printf("VAL 3 = %lld + %lld + %lld \n", t1.d[x], val[x], d[x]);
}*/
C[x] = VAL[x];
for(register int i(E[x]); i; i = EDGE[i].nex) {
int y = EDGE[i].v;
dfs_2(y);
SIZ[x] += SIZ[y];
C[x] = std::max(C[x], C[y]);
}
//if(F) printf("x = %d VAL = %lld C = %lld \n", x, VAL[x], C[x]);
return;
} void dfs_3(int x, int f) {
if(f) {
/// cal H[x]
H[x] = seg::getMax(POS[f], POS[x] - );
if(POS[x] + SIZ[x] < POS[f] + SIZ[f]) {
H[x] = std::max(H[x], seg::getMax(POS[x] + SIZ[x], POS[f] + SIZ[f] - ));
}
W[x] = std::max(W[f], H[x] - * t1::d[f]);
}
else {
H[x] = W[x] = -INF;
}
//if(F) printf("x = %d H = %lld W = %lld \n", x, H[x], W[x]);
for(register int i(E[x]); i; i = EDGE[i].nex) {
int y(EDGE[i].v);
dfs_3(y, x);
}
return;
} void DFS_4(int x, int f, LL v) {
/// ask
if(x <= n) {
VAL[x] = t1::d[x] + val[x] + d[x] + v;
ans = std::max(ans, VAL[x] + W[x]);
ans = std::max(ans, VAL[x] + C[x] - * t1::d[x]);
}
forson(x, i) {
int y(edge[i].v);
if(y == f || edge[i].vis) continue;
DFS_4(y, x, v);
}
return;
} void e_div(int x) {
if(_n == ) {
ans = std::max(ans, val[x] << );
return;
}
small = N;
getroot(x, );
edge[root].vis = edge[root ^ ].vis = ; x = edge[root].v;
int y = edge[root ^ ].v;
if(siz[x] < _n - siz[x]) std::swap(x, y); //if(F) printf("div %d %d _n = %d \n", x, y, _n); ///
d[x] = d[y] = ;
TP = K = ;
Time++;
DFS_1(x, , );
DFS_1(y, , );
/// build virtue trEE
build_t();
//printf("v_t build over \n");
/// prework
NUM = ;
dfs_2(RT);
//printf("dfs_2 over \n");
seg::build(NUM);
//printf("seg build over \n");
dfs_3(RT, );
DFS_4(y, , edge[root].len); //if(F) printf("ans = %d \n", ans); _n = siz[x];
e_div(x);
_n = siz[y];
e_div(y);
return;
} int main() { W[] = -INF;
read(n);
LL z;
for(register int i(), x, y; i < n; i++) {
read(x); read(y); read(z);
G[x].push_back(y);
G[y].push_back(x);
Len[x].push_back(z);
Len[y].push_back(z);
}
for(register int i(), x, y; i < n; i++) {
read(x); read(y); read(z);
t1::edge[++t1::tp].v = y;
t1::edge[t1::tp].len = z;
t1::edge[t1::tp].nex = t1::e[x];
t1::e[x] = t1::tp;
t1::edge[++t1::tp].v = x;
t1::edge[t1::tp].len = z;
t1::edge[t1::tp].nex = t1::e[y];
t1::e[y] = t1::tp;
}
for(register int i = ; i <= n * ; i++) {
pw[i] = pw[i >> ] + ;
}
t1::DFS_1(, );
t1::prework(); Cnt = n;
rebuild(, );
for(register int i(); i <= n; i++) {
val[i] = d[i] - t1::d[i];
}
/// ans = val[i] + val[j] + t0.dis(i, j) + t1.dis(i, j);
_n = Cnt;
e_div(); printf("%lld\n", ans >> );
return ;
}

AC代码

讲一下我的做法。考虑到两个点的距离是d + d - 2 * lca,我们预处理某一颜色的节点,然后枚举另一个颜色的所有点。

由于开店的启发,我们可以把每个点往上更新链,然后每个点往上查询链。

进而发现,查询全是静态的,所以可以预处理出一个点到根路径上的max,变成单点查询。

设VAl[x]为挂上的节点x的深度。C[x]为x的子树中的最大VAL值。

设H[x]为(subtree(fa[x]) \ subtree(x)的最大VAL值) - 2 * VAL[x],W[x]为x到根路径上的最大H值。

那么对于一个点x,它只要和W[x],C[x] - 2 * VAL[x]拼起来就能得到跟它相关的最远点对距离了。

复杂度在于求H[x]的时候使用DFS序 + RMQ,写了一个log。所以总复杂度是nlog2n。


有个T1是链且v > 0的部分分。这里我们发现T1的路径并变成了max(d1[x], d1[y]),于是考虑枚举T2中每个点作为lca,要求两个点在其子树中,且max(d1[x], d1[y])最大。显然维护一个子树最大值就好了。实测有80分呢......

还有个迭代乱搞是随机选一个起点,每次迭代找到一个点与之贡献最大并更新那个点为新的起点。多来几次。这样也有80分呢......

比写正解不知道高到哪里去了...

LOJ#2553 暴力写挂的更多相关文章

  1. [LOJ#2553][CTSC2018]暴力写挂

    [LOJ#2553][CTSC2018]暴力写挂 试题描述 temporaryDO 是一个很菜的 OIer .在 4 月,他在省队选拔赛的考场上见到了<林克卡特树>一题,其中 \(k = ...

  2. Loj #2553. 「CTSC2018」暴力写挂

    Loj #2553. 「CTSC2018」暴力写挂 题目描述 temporaryDO 是一个很菜的 OIer .在 4 月,他在省队选拔赛的考场上见到了<林克卡特树>一题,其中 \(k = ...

  3. BZOJ5341[Ctsc2018]暴力写挂——边分治+虚树+树形DP

    题目链接: CSTC2018暴力写挂 题目大意:给出n个点结构不同的两棵树,边有边权(有负权边及0边),要求找到一个点对(a,b)满足dep(a)+dep(b)-dep(lca)-dep'(lca)最 ...

  4. 【CTSC2018】暴力写挂(边分治,虚树)

    [CTSC2018]暴力写挂(边分治,虚树) 题面 UOJ BZOJ 洛谷 题解 发现第二棵树上的\(LCA\)的深度这玩意没法搞,那么枚举在第二棵树上的\(LCA\). 然后剩下的部分就是\(dep ...

  5. [CTSC2018]暴力写挂——边分树合并

    [CTSC2018]暴力写挂 题面不错 给定两棵树,两点“距离”定义为:二者深度相加,减去两棵树上的LCA的深度(深度指到根节点的距离) 求最大的距离. 解决多棵树的问题就是降维了. 经典的做法是边分 ...

  6. BZOJ5341: [Ctsc2018]暴力写挂

    BZOJ5341: [Ctsc2018]暴力写挂 https://lydsy.com/JudgeOnline/problem.php?id=5341 分析: 学习边分治. 感觉边分治在多数情况下都能用 ...

  7. 「CTSC2018」暴力写挂

    毫无$ Debug$能力 全世界就我会被卡空间.jpg LOJ #2553 UOJ #400 Luogu P4565 题意 给定两棵树$ T,T'$,求一组点对$ (x,y)$使得$deep(x)+d ...

  8. LOJ 2553 「CTSC2018」暴力写挂——边分治+虚树

    题目:https://loj.ac/problem/2553 第一棵树上的贡献就是链并,转化成 ( dep[ x ] + dep[ y ] + dis( x, y ) ) / 2 ,就可以在第一棵树上 ...

  9. LOJ #2533. 「CTSC2018」暴力写挂(边分治合并)

    题意 给你两个有 \(n\) 个点的树 \(T, T'\) ,求一对点对 \((x, y)\) 使得 \[ depth(x) + depth(y) - (depth(LCA(x , y)) + dep ...

随机推荐

  1. 关于在Python2中使用列表推导式会遇到的问题

    摘自<流畅的Python>第二部分第二章2.2 Python 2.x 中,在列表推导中 for 关键词之后的赋值操作可能会影响列表推导上下文中的同名变量.像下面这个 Python 2.7 ...

  2. 安装MySQL8.0 遇到的3个小错误

    过去公司都是用的5.7 系列的MySQL,随着8.0的发版,也想试着升级一下.遇到了两个小错误,记录在此. 路径设置: 安装包路径:/data/mysql80/ 数据路径: /data/mysql/ ...

  3. Oracle根据符合条件的数据循环批量更新

    --批量对符合条件的表记录进行更新 --aa代表查询出的符合条件数据的别名 --aa后的表示需要符合的条件 --loop后开始写更新操作 begin for aa in (select a.objec ...

  4. 【eclipse】mybatis配置文件创建与mapper接口文件创建

    什么是mybatis: MyBatis 是一款优秀的持久层框架,它支持定制化 SQL.存储过程以及高级映射. mybatis配置文件: <?xml version="1.0" ...

  5. MFC映射

    所有CDC输出函数最终都会输出到物理平面(屏幕窗口.打印纸等).这些物理平面的单位量化往往多种多样,比如像素.打印点.英寸.毫米等等.这样可能会造成很多混乱,所以CDC输出对所有物理平面进行统一抽象化 ...

  6. 闭包函数&回调函数

    闭包函数&回调函数 谈到回调函数,不得不提匿名函数;匿名函数,也叫闭包函数,也就是没有名字的函数,它可以单独存在,也可以将其赋值给某一个变量.so,先来看一下闭包函数. 闭包函数 php文档: ...

  7. 5.4Python数据处理篇之Sympy系列(四)---微积分

    目录 目录 前言 (一)求导数-diff() 1.一阶求导-diff() 2.多阶求导-diff() 3.求偏导数-diff() (二)求积分-integrate() (三)求极限-limit() ( ...

  8. MySql 学习之路-Date函数

    MySQL中重要的内建函数 函数 描述 NOW() 返回当前的日期和时间 NOW() 返回当前的日期和时间. 语法 NOW() -- 实例 -- 下面是 SELECT 语句: SELECT NOW() ...

  9. Windows使用MongoDB,以及索引创建

    安装MongoDB https://www.mongodb.com/download-center#community 点击msi安装程序进行安装,可以进行自定义安装,选择安装位置,我选择的是D盘 在 ...

  10. STM32 FSMC使用笔记

    最近在使用STM32的FSMC与FPGA做并行通信总线控制,做一下总结 1,利用FSMC读取写入16位数据时的封装函数如下,不这样使用的话在与FPGA进行通信的过程中可能会出现不可预知的错误. #de ...