LOJ#2553 暴力写挂
题意:给定两棵树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 暴力写挂的更多相关文章
- [LOJ#2553][CTSC2018]暴力写挂
[LOJ#2553][CTSC2018]暴力写挂 试题描述 temporaryDO 是一个很菜的 OIer .在 4 月,他在省队选拔赛的考场上见到了<林克卡特树>一题,其中 \(k = ...
- Loj #2553. 「CTSC2018」暴力写挂
Loj #2553. 「CTSC2018」暴力写挂 题目描述 temporaryDO 是一个很菜的 OIer .在 4 月,他在省队选拔赛的考场上见到了<林克卡特树>一题,其中 \(k = ...
- BZOJ5341[Ctsc2018]暴力写挂——边分治+虚树+树形DP
题目链接: CSTC2018暴力写挂 题目大意:给出n个点结构不同的两棵树,边有边权(有负权边及0边),要求找到一个点对(a,b)满足dep(a)+dep(b)-dep(lca)-dep'(lca)最 ...
- 【CTSC2018】暴力写挂(边分治,虚树)
[CTSC2018]暴力写挂(边分治,虚树) 题面 UOJ BZOJ 洛谷 题解 发现第二棵树上的\(LCA\)的深度这玩意没法搞,那么枚举在第二棵树上的\(LCA\). 然后剩下的部分就是\(dep ...
- [CTSC2018]暴力写挂——边分树合并
[CTSC2018]暴力写挂 题面不错 给定两棵树,两点“距离”定义为:二者深度相加,减去两棵树上的LCA的深度(深度指到根节点的距离) 求最大的距离. 解决多棵树的问题就是降维了. 经典的做法是边分 ...
- BZOJ5341: [Ctsc2018]暴力写挂
BZOJ5341: [Ctsc2018]暴力写挂 https://lydsy.com/JudgeOnline/problem.php?id=5341 分析: 学习边分治. 感觉边分治在多数情况下都能用 ...
- 「CTSC2018」暴力写挂
毫无$ Debug$能力 全世界就我会被卡空间.jpg LOJ #2553 UOJ #400 Luogu P4565 题意 给定两棵树$ T,T'$,求一组点对$ (x,y)$使得$deep(x)+d ...
- LOJ 2553 「CTSC2018」暴力写挂——边分治+虚树
题目:https://loj.ac/problem/2553 第一棵树上的贡献就是链并,转化成 ( dep[ x ] + dep[ y ] + dis( x, y ) ) / 2 ,就可以在第一棵树上 ...
- LOJ #2533. 「CTSC2018」暴力写挂(边分治合并)
题意 给你两个有 \(n\) 个点的树 \(T, T'\) ,求一对点对 \((x, y)\) 使得 \[ depth(x) + depth(y) - (depth(LCA(x , y)) + dep ...
随机推荐
- SpringBoot实现全文搜索
• 全文搜索 • solr安装 • solr中文分词 • solr数据库导入 • solr数据查询 • solrj接口调用 1:
- spring学习总结——装配Bean学习四(导入和混合配置)
情景:在典型的Spring应用中,我们可能会同时使用自动化和显式配置(JavaConfig)或者XML配置,幸好在Spring中,这些配置方案都不是互斥的.你尽可以将JavaConfig的组件扫描和自 ...
- Canadian-dollar_RMB
import pandas as pd import matplotlib.pyplot as plt import statsmodels as sm from statsmodels.graphi ...
- Django组件--分页器(有用)
一.分页器对象 from django.core.paginator import Paginator,EmptyPage book_list = Book.objects.all() #假设有100 ...
- RESTful API规范
1. 域名 应该尽量将API部署在专用的域名下. https://api.example.com 如果确定API简单,不会有进一步的括在,可以考虑放在主域名之下. https://example.or ...
- .NET CORE学习笔记系列(2)——依赖注入[6]: .NET Core DI框架[编程体验]
原文https://www.cnblogs.com/artech/p/net-core-di-06.html 毫不夸张地说,整个ASP.NET Core框架是建立在一个依赖注入框架之上的,它在应用启动 ...
- 使用 ESP8266 制作 WiFi 干扰器 - 无需密码即可使用任何 WiFi
嘿,朋友,我是 Kedar,你有没有想阻止所有的 WiFi信号?或者只是想从 WiFi 踢某人或邻居 WiFi .那么,本玩法是你等待结束的时刻了.这是为你提供的.仅需 $8 的 DIY Wifi 干 ...
- 回顾servlet生命周期(代码测试),读取初始化参数
servlet生命周期 为简洁,本例使用注解方式来测试,代码部分很简单,只需要新建一个serlet,继承自HttpServlet,重写init,doGet,doPost,destory方法即可,使用注 ...
- 问题记录2019-03-06(todo)
RuntimeError: maximum recursion depth exceeded while calling a Python object
- c# 7.0 6.0 新语法
1.参考地址:https://docs.microsoft.com/zh-cn/dotnet/csharp/tutorials/exploration/csharp-7?tutorial-step=5 ...