暴力写挂
Description
temporaryDO 是一个很菜的 OIer 。在 4 月,他在省队选拔赛的考场上见到了《林克卡特树》一题,其中 k = 0k=0 的部分分是求树 TT 上的最长链。可怜的 temporaryDO 并不会做这道题,他在考场上抓猫耳挠猫腮都想不出一点思路。
这时,善良的板板出现在了空中,他的身上发出璀璨却柔和的光芒,荡漾在考场上。“题目并不难。” 板板说。那充满磁性的声音,让 temporaryDO 全身充满了力量。
他决定:写一个枚举点对求 LCA 算距离的 k=0 k = 0 的 O(n2 log n) O ( n 2 l o g n ) 的部分分程序!于是, temporaryDO 选择以 11 为根,建立了求 LCA 的树链剖分结构,然后写了二重 for 循环枚举点对。
然而,菜菜的 temporaryDO 不小心开小了数组,于是数组越界到了一片神秘的内存区域。但恰好的是,那片内存区域存储的区域恰好是另一棵树 T′ T ′ 。这样一来,程序并没有 RE ,但他求 xx 和 yy 的距离的时候,计算的是
depth(x)+depth(y)−(depth(LCA(x,y))+depth′(LCA′(x,y))) d e p t h ( x ) + d e p t h ( y ) − ( d e p t h ( L C A ( x , y ) ) + d e p t h ′ ( L C A ′ ( x , y ) ) )
最后程序会输出每一对点对 i,j(i≤j) i , j ( i ≤ j ) 的如上定义的“距离” 的最大值。 temporaryDO 的程序在评测时光荣地爆零了。但他并不服气,他决定花好几天把自己的程序跑出来。请你根据 T T 和 帮帮可怜的 temporaryDO 求出他程序的输出。
Input
第一行包含一个整数
n
n
,表示树上的节点个数;
第 到第
n
n
行,每行三个整数 ,表示
T
T
中存在一条从 到
y
y
的边,其长度为 ;第
n+1
n
+
1
到第
2n−1
2
n
−
1
,每行三个整数
x,y,v
x
,
y
,
v
,表示
T′
T
′
中存在一条从
x
x
到 的边,其长度为
v
v
。
Output
输出一行一个整数,表示 temporaryDO 的程序的输出。
Sample Input
6
1 2 2
1 3 0
2 4 1
2 5 -7
3 6 0
1 2 -1
2 3 -1
2 5 3
2 6 -2
3 4 8
Sample Output
5
HINT
点对 (3, 4)(3,4) 的距离计算为 3 + 0 - (0 + (-2)) = 53+0−(0+(−2))=5 。
对于所有数据,。
depth(p) 和 depth’(p) 分别表示树 T T 、 中点 1 1 到点 的距离,这里规定,距离指的是经过的边的边权总和,其中 depth(1)=0 d e p t h ( 1 ) = 0 。 LCA(x, y) 和 LCA’(x, y) 分别表示树 T T 、 中点 xx 与点 y y 的最近公共祖先,即在从 到 y y 的最短路径上的距离根经过边数最少的点。
猫式树上最优化问题系列的特性:写正解的性价比远低于乱搞做法……
沉迷于颓废导致写了一上午假做法却没意识到,最后又花了一下午写正解……
思路:
首先你得知道什么是边分治,并且知道这题要用。
然后想必各种思路就出来了。
首先说说咱的假做法。
这个第二棵树的 好像不太兹瓷化简,那么考虑枚举第二颗树的
lca
l
c
a
。
于是化简第一棵树相关内容:
突然舒服.jpg
然后就可以搞事了。
考虑建出一棵边分树,用它维护一些信息。
对于一条分治过程中的边
p=up,vp
p
=
u
p
,
v
p
,设它将当前分治子树分成两个点集
U,V
U
,
V
,那么需要维护两个信息:
lmax[p]=max(dep[u]+dist(u,up)|u∈U)
l
m
a
x
[
p
]
=
m
a
x
(
d
e
p
[
u
]
+
d
i
s
t
(
u
,
u
p
)
|
u
∈
U
)
和
rmax[p]=max(dep[v]+dist(v,vp)|v∈V)
r
m
a
x
[
p
]
=
m
a
x
(
d
e
p
[
v
]
+
d
i
s
t
(
v
,
v
p
)
|
v
∈
V
)
.
这样便可支持
O(logn)
O
(
l
o
g
n
)
询问一个点与一个点集之间的最大方案。
具体方法是,找到深度最深的一条以询问点为端点的边,暴力向上跳,每次在每层的另一个点集处询问最大值并更新答案即可。
于是就可以考虑遍历第二棵树的每个节点作为
lca
l
c
a
并计算贡献了。
考虑难以在点分树上删除,使用树上启发式合并维护,即继承重儿子的点集,而当遍历完非重儿子时,直接清空整棵边分树即可。
复杂度 O(nlog2n) O ( n l o g 2 n ) ,用尽全力卡常极限数据也要 8.0s 8.0 s ……
接下来是正解:
为什么要启发式合并呢?
听说过线段树合并么?
可以发现边分树是二叉的,形如线段树,那么写个边分树合并不就好了!
答案可以方便地在合并过程中统计。
复杂度
O(nlogn)
O
(
n
l
o
g
n
)
,快如……好吧也没比
O(nlog2n)
O
(
n
l
o
g
2
n
)
快多少……
一个
3e5
3
e
5
的
log
l
o
g
只快了四倍多,这丢人的常数……不过还是能过的
放代码。
首先是本zz的85分
O(nlog2n)
O
(
n
l
o
g
2
n
)
辣鸡做法:
#include<cstdio>
#include<vector>
#include<algorithm>
using namespace std;
typedef long long ll;
typedef pair<int,int> pr;
const int N=377779;
const int K=23;
inline int read()
{
int x=0,f=1;char ch=getchar();
while(ch<'0' || '9'<ch){if(ch=='-')f=-1;ch=getchar();}
while('0'<=ch && ch<='9')x=x*10+(ch^48),ch=getchar();
return x*f;
}
template<typename T> inline bool chkmin(T &a,T b){if(a>b){a=b;return 1;}return 0;}
template<typename T> inline bool chkmax(T &a,T b){if(a<b){a=b;return 1;}return 0;}
int to[N<<1],nxt[N<<1],w[N<<1],beg[N],tot;
int n,id[N],ed[N],seg[N],dfn;
int siz[N],son[N],fa[N];
ll dep[N],ans=-1e18;
inline void add(int u,int v,int c)
{
to[++tot]=v;nxt[tot]=beg[u];beg[u]=tot;w[tot]=c;
}
namespace tdc
{
const int P=N<<1;
int to[P<<1],nxt[P<<1],w[P<<1],beg[P],tot=1;
int st[P<<1][K],id[P],bin[P<<1],depth[P],dfn;
int n,siz[P],ban[P<<1],bel[P];
ll dep[P],lmx[P<<1],rmx[P<<1];
int fat[P<<1],son[P<<1];
vector<pr> g[P];
inline void add(int u,int v,int c)
{
to[++tot]=v;nxt[tot]=beg[u];beg[u]=tot;w[tot]=c;
}
inline void dfs_init(int u,int fa)
{
st[id[u]=++dfn][0]=u;
for(int i=beg[u];i;i=nxt[i])
if(to[i]!=fa)
{
dep[to[i]]=dep[u]+w[i];
depth[to[i]]=depth[u]+1;
dfs_init(to[i],u);
st[++dfn][0]=u;
}
}
inline void build_lca()
{
for(int i=2;i<=dfn;i++)
bin[i]=bin[i>>1]+1;
for(int i=1;i<=bin[dfn];i++)
for(int j=1;j+(1<<i)-1<=dfn;j++)
{
if(depth[st[j][i-1]]<depth[st[j+(1<<i-1)][i-1]])
st[j][i]=st[j][i-1];
else
st[j][i]=st[j+(1<<i-1)][i-1];
}
}
inline int lca(int a,int b)
{
a=id[a],b=id[b];if(a>b)swap(a,b);
int dlt=bin[b-a+1];
return depth[st[a][dlt]]<depth[st[b-(1<<dlt)+1][dlt]]?st[a][dlt]:st[b-(1<<dlt)+1][dlt];
}
inline ll dist(int a,int b)
{
return dep[a]+dep[b]-(dep[lca(a,b)]<<1);
}
inline void build_tree(int u,int fa)
{
int lst=0;
for(int i=0;i<g[u].size();i++)
if(g[u][i].first!=fa)
{
if(!lst)
{
add(u,g[u][i].first,g[u][i].second);
add(g[u][i].first,u,g[u][i].second);
lst=u;
}
else
{
add(lst,++n,0);add(n,lst,0);lst=n;
add(lst,g[u][i].first,g[u][i].second);
add(g[u][i].first,lst,g[u][i].second);
}
build_tree(g[u][i].first,u);
}
}
inline int dfs_find(int u,int fa,int &rt,int &dlt,int tsz)
{
int sz=1;
for(int i=beg[u];i;i=nxt[i])
if(!ban[i] && to[i]!=fa)
{
sz+=(siz[i]=dfs_find(to[i],u,rt,dlt,tsz));
siz[i^1]=tsz-siz[i];
if(chkmin(dlt,abs(siz[i^1]-siz[i])))
rt=i;
}
return sz;
}
inline void work(int u,int tsz,int fa,int l)
{
if(tsz==1)return;
int rt=0,dlt=1e9+7;
dfs_find(u,0,rt,dlt,tsz);
ban[rt]=ban[rt^1]=1;
fat[rt]=fa;son[rt]=l;
lmx[rt]=rmx[rt]=-1e18;
bel[to[rt]]=rt;bel[to[rt^1]]=rt;
work(to[rt],siz[rt],rt,0);
work(to[rt^1],siz[rt^1],rt,1);
}
inline void init()
{
build_tree(1,0);
dfs_init(1,0);
build_lca();
work(1,n,0,0);
}
inline void del(int u)
{
int p=bel[u],dir;
if(to[p]==u)
lmx[p]=-1e18;
else
rmx[p]=-1e18;
for(dir=son[p],p=fat[p];p;dir=son[p],p=fat[p])
(!dir?lmx[p]:rmx[p])=-1e18;
}
inline void add(int u)
{
int p=bel[u],dir;
if(to[p]==u)
chkmax(lmx[p],dep[u]);
else
chkmax(rmx[p],dep[u]);
for(dir=son[p],p=fat[p];p;dir=son[p],p=fat[p])
chkmax((!dir?lmx[p]:rmx[p]),dist(u,to[p^dir])+dep[u]);
}
inline ll query(int u)
{
int p=bel[u],dir;ll ret=-1e18;
chkmax(ret,lmx[p]+dep[u]+(to[p]!=u)*w[p]);
chkmax(ret,rmx[p]+dep[u]+(to[p]==u)*w[p]);
for(dir=son[p],p=fat[p];p!=0;dir=son[p],p=fat[p])
chkmax(ret,dep[u]+(!dir?rmx[p]:lmx[p])+dist(to[p^1^dir],u));
return ret;
}
}
inline void dfs_pre(int u,int fa)
{
seg[id[u]=++dfn]=u;siz[u]=1;
for(int i=beg[u];i;i=nxt[i])
if(to[i]!=fa)
{
dep[to[i]]=dep[u]+w[i];
dfs_pre(to[i],u);
siz[u]+=siz[to[i]];
if(!son[u] || siz[son[u]]<siz[to[i]])
son[u]=to[i];
}
ed[u]=dfn;
}
inline void dfs_work(int u,int fa)
{
for(int i=beg[u];i;i=nxt[i])
if(to[i]!=fa && to[i]!=son[u])
dfs_work(to[i],u);
if(son[u])dfs_work(son[u],u);
tdc::add(u);
for(int i=beg[u];i;i=nxt[i])
if(to[i]!=fa && to[i]!=son[u])
{
for(int j=id[to[i]];j<=ed[to[i]];j++)
chkmax(ans,tdc::query(seg[j])/2-dep[u]);
for(int j=id[to[i]];j<=ed[to[i]];j++)
tdc::add(seg[j]);
}
chkmax(ans,tdc::query(u)/2-dep[u]);
if(fa && son[fa]!=u)
for(int i=id[u];i<=ed[u];i++)
tdc::del(seg[i]);
}
int main()
{
tdc::n=n=read();
for(int i=1,a,b,c;i<n;i++)
{
a=read();b=read();c=read();
tdc::g[a].push_back(pr(b,c));
tdc::g[b].push_back(pr(a,c));
}
for(int i=1,a,b,c;i<n;i++)
{
a=read();b=read();c=read();
add(a,b,c);add(b,a,c);
}
tdc::init();
dfs_pre(1,0);
dfs_work(1,0);
printf("%lld\n",ans);
return 0;
}
然后是神奇的100分 O(nlogn) O ( n l o g n ) 做法:(感谢xzk dalao 的提示不然咱可能一辈子85pts)
#include<cstdio>
#include<vector>
#include<algorithm>
using namespace std;
typedef long long ll;
typedef pair<int,int> pr;
const int N=377779;
const int K=23;
inline int read()
{
int x=0,f=1;char ch=getchar();
while(ch<'0' || '9'<ch){if(ch=='-')f=-1;ch=getchar();}
while('0'<=ch && ch<='9')x=x*10+(ch^48),ch=getchar();
return x*f;
}
template<typename T> inline bool chkmin(T &a,T b){if(a>b){a=b;return 1;}return 0;}
template<typename T> inline bool chkmax(T &a,T b){if(a<b){a=b;return 1;}return 0;}
int n,siz[N],fa[N],rt[N];
ll dep[N],ans=0;
vector<pr> g[N];
namespace tdc
{
const int P=N<<1;
const int M=N*20;
int to[P<<1],nxt[P<<1],w[P<<1],beg[P],tot=1;
int st[P<<1][K],id[P],bin[P<<1],depth[P],dfn;
int n,siz[P<<1],ban[P<<1],bel[P],son[P<<1][2];
vector<pr> g[P];
ll dep[P];
int stk[M],top;
ll lmx[M],rmx[M];
int cnt,fat[M],ind[M],ls[M],rs[M];
inline void add(int u,int v,int c)
{
to[++tot]=v;nxt[tot]=beg[u];beg[u]=tot;w[tot]=c;
}
inline void dfs_init(int u,int fa)
{
st[id[u]=++dfn][0]=u;
for(int i=beg[u];i;i=nxt[i])
if(to[i]!=fa)
{
dep[to[i]]=dep[u]+w[i];
depth[to[i]]=depth[u]+1;
dfs_init(to[i],u);
st[++dfn][0]=u;
}
}
inline void build_lca()
{
for(int i=2;i<=dfn;i++)
bin[i]=bin[i>>1]+1;
for(int i=1;i<=bin[dfn];i++)
for(int j=1;j+(1<<i)-1<=dfn;j++)
{
if(depth[st[j][i-1]]<depth[st[j+(1<<i-1)][i-1]])
st[j][i]=st[j][i-1];
else
st[j][i]=st[j+(1<<i-1)][i-1];
}
}
inline int lca(int a,int b)
{
a=id[a],b=id[b];if(a>b)a^=b,b^=a,a^=b;int dlt=bin[b-a+1];
return depth[st[a][dlt]]<depth[st[b-(1<<dlt)+1][dlt]]?st[a][dlt]:st[b-(1<<dlt)+1][dlt];
}
inline ll dist(int a,int b)
{
return dep[a]+dep[b]-(dep[lca(a,b)]<<1);
}
inline void build_tree(int u,int fa)
{
int lst=0;
for(int i=0;i<g[u].size();i++)
if(g[u][i].first!=fa)
{
if(!lst)
{
add(u,g[u][i].first,g[u][i].second);
add(g[u][i].first,u,g[u][i].second);
lst=u;
}
else
{
add(lst,++n,0);add(n,lst,0);lst=n;
add(lst,g[u][i].first,g[u][i].second);
add(g[u][i].first,lst,g[u][i].second);
}
build_tree(g[u][i].first,u);
}
}
inline int dfs_find(int u,int fa,int &rt,int &dlt,int tsz)
{
int sz=1;
for(int i=beg[u];i;i=nxt[i])
if(!ban[i] && to[i]!=fa)
{
sz+=(siz[i]=dfs_find(to[i],u,rt,dlt,tsz));
siz[i^1]=tsz-siz[i];
if(chkmin(dlt,abs(siz[i^1]-siz[i])))
rt=i;
}
return sz;
}
inline int work(int u,int tsz,int fa)
{
if(tsz==1)return 0;
int rt=0,dlt=1e9+7;
dfs_find(u,0,rt,dlt,tsz);
ban[rt]=ban[rt^1]=1;fat[rt]=fa;
lmx[rt]=rmx[rt]=-1e18;
bel[to[rt]]=rt;bel[to[rt^1]]=rt;
son[rt][0]=work(to[rt],siz[rt],rt);
son[rt][1]=work(to[rt^1],siz[rt^1],rt);
return rt;
}
inline void init()
{
build_tree(1,0);
dfs_init(1,0);
build_lca();
work(1,n,0);
for(int i=1;i<M;i++)
stk[++top]=i;
}
inline int newnode(int p)
{
int ret=stk[top--];
lmx[ret]=rmx[ret]=-1e18;
ls[ret]=rs[ret]=0;
ind[ret]=p;return ret;
}
inline int build(int u)
{
int now=0;
for(int p=bel[u],dir=(to[p]!=u),ch=0;p;ch=now,p=fat[p])
{
now=newnode(p);
chkmax((!dir?lmx[now]:rmx[now]),dist(u,to[p^dir])+dep[u]);
if(ch)(son[p][1]==ind[ch]?rs[now]:ls[now])=ch;
dir=son[fat[p]][1]==p;
}
return now;
}
inline int merge(int x,int y,ll p)
{
if(!x || !y)return x|y;
chkmax(ans,(lmx[x]+rmx[y]+w[ind[x]])/2-p);
chkmax(ans,(rmx[x]+lmx[y]+w[ind[x]])/2-p);
chkmax(lmx[x],lmx[y]);ls[x]=merge(ls[x],ls[y],p);
chkmax(rmx[x],rmx[y]);rs[x]=merge(rs[x],rs[y],p);
if(y)stk[++top]=y;return x;
}
}
inline void dfs_pre(int u,int fa)
{
for(int i=0,v;i<g[u].size();i++)
if((v=g[u][i].first)!=fa)
{
dep[v]=dep[u]+g[u][i].second;
dfs_pre(v,u);
siz[u]+=siz[v];
}
}
inline bool cmp(pr a,pr b)
{
return siz[a.first]<siz[b.first];
}
inline void dfs_work(int u,int fa)
{
rt[u]=tdc::build(u);
chkmax(ans,tdc::dep[u]-dep[u]);
sort(g[u].begin(),g[u].end(),cmp);
for(int i=0,v;i<g[u].size();i++)
if((v=g[u][i].first)!=fa)
{
dfs_work(v,u);
rt[u]=tdc::merge(rt[u],rt[v],dep[u]);
}
}
int main()
{
tdc::n=n=read();
for(int i=1,a,b,c;i<n;i++)
{
a=read();b=read();c=read();
tdc::g[a].push_back(pr(b,c));
tdc::g[b].push_back(pr(a,c));
}
for(int i=1,a,b,c;i<n;i++)
{
a=read();b=read();c=read();
g[a].push_back(pr(b,c));
g[b].push_back(pr(a,c));
}
tdc::init();
dfs_pre(1,0);
dfs_work(1,0);
printf("%lld\n",ans);
return 0;
}