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

[CTSC2018]暴力写挂

题面不错

给定两棵树,两点“距离”定义为:二者深度相加,减去两棵树上的LCA的深度(深度指到根节点的距离)

求最大的距离。

解决多棵树的问题就是降维了。

经典的做法是边分树合并

边分树结构类似0/1 trie

就是把边分树对于每个点拆开路径

合并两棵边分树同时可以得到两个边分树之间点对的路径的信息

感觉有点类似线段树合并。

根据“猫树”思想,两点间的路径一定经过边分树上LCA的那条边。(u,v不相等)

我们考虑在这个LCA处统计贡献

具体地,先对1树进行边分治

每个点初始的边分树是一条链,考虑对每个点构造出这个边分树。

开始只有根。

其实就是记录分治时候是在那个位置。

定义连接分治重心root深度较小的连通块为右部点,另一个为左部点

保存每个点作为左部点还是右部点

在每个之前最后一个加入位置lasi 下面加入左儿子或者右儿子。

在lasi位置保留这个信息vl,vr。初始是-inf

表示子树里所有的真实点在边分治这一层的左部、右部最大值。

左部点贡献权值:dis[x]

右部点贡献:dis[x]-dis[lca]

因为lca一定在右部点。

在第二棵树上枚举LCA z ,子树边分树合并上来(就类似树上的线段树合并)

合并时候max(vl(x)+vr(y)-dis‘[z],vr(x)+vl(y)-dis‘[z])更新答案。

然后vl(x)=max(vl(x),vl(y)) vr同理。按位取max

(注意没有pushup,因为这里是分治结构

可以发现,任意点对(u,v),一定在第二棵树上的LCA位置被考虑到,边分树合并时候,会在边分树LCA处尝试做出贡献。

大概初始的分治树:


代码:

注意:

1.边分树2*N个点,边数4*N,

边分治的vis数组开4*N。

2.处理u,v重合情况。

// luogu-judger-enable-o2
// luogu-judger-enable-o2
#include<bits/stdc++.h>
#define reg register int
#define il inline
#define fi first
#define se second
#define mk(a,b) make_pair(a,b)
#define numb (ch^‘0‘)
using namespace std;
typedef long long ll;
template<class T>il void rd(T &x){
    char ch;x=0;bool fl=false;
    while(!isdigit(ch=getchar()))(ch==‘-‘)&&(fl=true);
    for(x=numb;isdigit(ch=getchar());x=x*10+numb);
    (fl==true)&&(x=-x);
}
template<class T>il void output(T x){if(x/10)output(x/10);putchar(x%10+‘0‘);}
template<class T>il void ot(T x){if(x<0) putchar(‘-‘),x=-x;output(x);putchar(‘ ‘);}
template<class T>il void prt(T a[],int st,int nd){for(reg i=st;i<=nd;++i) ot(a[i]);putchar(‘\n‘);}

namespace Miracle{
const int N=366666+6;
const ll inf=1e18;
int n;

struct tr{
    int ls,rs;
    ll vl,vr;
    tr(){
        ls=0;rs=0;vl=vr=-inf;
    }
}t[N*30];
ll ans;
int tot;
int rt[N];
ll nd;//now dis of lca‘(u,v)
int merge(int x,int y){
    // cout<<" merge "<<x<<" "<<y<<endl;
    if(!x||!y) return x+y;
    ans=max(ans,max(t[x].vl+t[y].vr,t[x].vr+t[y].vl)-nd);
    t[x].ls=merge(t[x].ls,t[y].ls);
    t[x].rs=merge(t[x].rs,t[y].rs);
    t[x].vl=max(t[x].vl,t[y].vl);
    t[x].vr=max(t[x].vr,t[y].vr);
    return x;
}

namespace tr1{
vector<int>to[N],val[N];
ll dis[2*N];
struct node{
    int nxt,to;
    int val;
}e[4*N];
int hd[2*N],cnt=1;
void add(int x,int y,int z){
    e[++cnt].nxt=hd[x];
    e[cnt].to=y;
    hd[x]=cnt;
    e[cnt].val=z;
}
int cur;
int d[2*N];
void rebuild(int x,int fa){
    int las=x;
    for(reg i=0;i<(int)to[x].size();++i){
        int y=to[x][i];
        if(y==fa) continue;
        ++cur;
        add(las,cur,0);
        add(cur,las,0);
        add(cur,y,val[x][i]);
        add(y,cur,val[x][i]);
        las=cur;
        rebuild(y,x);
    }
}
void dfs(int x,int fa){
    // cout<<" dfs tr1 "<<x<<" "<<fa<<endl;
    d[x]=d[fa]+1;
    for(reg i=hd[x];i;i=e[i].nxt){
        int y=e[i].to;
        if(y==fa) continue;
        dis[y]=dis[x]+e[i].val;
        dfs(y,x);
    }
}
int nowsz;
int vis[4*N];
int las[2*N];
int root,sz[2*N];
int mi;
void fin(int x,int fa){
    sz[x]=1;
    for(reg i=hd[x];i;i=e[i].nxt){
        int y=e[i].to;
        if(vis[i]||y==fa) continue;
        fin(y,x);
        if(mi>max(sz[y],nowsz-sz[y])){
            mi=max(sz[y],nowsz-sz[y]);
            root=i;
        }
        sz[x]+=sz[y];
    }
}
void dfs2(int x,int fa,int id,int typ){//min d‘s id//typ==0 : le ; typ==1 ri
    sz[x]=1;
    if(d[id]>d[x]) id=x;
    if(x<=n){
        if(typ==0){
            ++tot;
            t[las[x]].ls=tot;
            t[las[x]].vl=dis[x];
            las[x]=tot;
        }else{
            ++tot;
            t[las[x]].rs=tot;
            t[las[x]].vr=dis[x]-dis[id];
            las[x]=tot;
        }
    }

    for(reg i=hd[x];i;i=e[i].nxt){
        int y=e[i].to;
        if(vis[i]||y==fa) continue;
        dfs2(y,x,id,typ);
        sz[x]+=sz[y];
    }
}
void divi(int x){
    if(nowsz==1) return;
    root=0;
    mi=0x3f3f3f3f;
    fin(x,0);
    // cout<<" root "<<root<<" x "<<x<<endl;
    int le=e[root].to,ri=e[root^1].to;
    if(d[le]<d[ri]) swap(le,ri);
    // cout<<" le "<<le<<" ri "<<ri<<endl;

    vis[root]=vis[root^1]=1;
    dfs2(le,0,0,0);
    dfs2(ri,0,0,1);
    nowsz=sz[le];
    divi(le);
    nowsz=sz[ri];
    divi(ri);
}
void che(int x){
    if(!x) return;
    cout<<" nowcur "<<x<<endl;
    cout<<" vl "<<t[x].vl<<" vr "<<t[x].vr<<" ls "<<t[x].ls<<" rs "<<t[x].rs<<endl;
    che(t[x].ls);che(t[x].rs);
}
void main(){
    int x,y,z;

    for(reg i=1;i<n;++i){
        rd(x);rd(y);rd(z);
        to[x].push_back(y);val[x].push_back(z);
        to[y].push_back(x);val[y].push_back(z);
    }
    cur=n;
    rebuild(1,0);
    // cout<<" rb "<<endl;
    dfs(1,0);
    // prt(dis,1,cur);
    // prt(d,1,cur);
    // cout<<" dfs "<<endl;
    d[0]=0x3f3f3f3f;

    nowsz=cur;
    for(reg i=1;i<=n;++i){
        rt[i]=++tot;
        las[i]=tot;
    }
    divi(1);
    // che(3);
    // che(4);

    // cout<<" divi "<<endl;
}

}
namespace tr2{
ll dis[N];
struct node{
    int nxt,to;
    int val;
}e[2*N];
int hd[N],cnt=0;
void add(int x,int y,int z){
    e[++cnt].nxt=hd[x];
    e[cnt].to=y;
    hd[x]=cnt;
    e[cnt].val=z;
}
void dfs(int x,int fa){
    for(reg i=hd[x];i;i=e[i].nxt){
        int y=e[i].to;
        if(y==fa) continue;
        dis[y]=dis[x]+e[i].val;
        dfs(y,x);
        nd=dis[x];
        // cout<<" xx "<<x<<" nd "<<nd<<endl;
        rt[x]=merge(rt[x],rt[y]);
    }
    ans=max(ans,tr1::dis[x]-dis[x]);
}
void main(){
    int x,y,z;
    for(reg i=1;i<n;++i){
        rd(x);rd(y);rd(z);
        add(x,y,z);add(y,x,z);
    }
    ans=-inf;
    dfs(1,0);
}

}
int main(){
    rd(n);
    tr1::main();
    tr2::main();
    ot(ans);
    return 0;
}

}
signed main(){
    Miracle::main();
    return 0;
}

/*
   Author: *Miracle*
   Date: 2019/4/13 19:58:12
*/

合并时候,就是利用分治树的结构层层分离点对,在分治边的位置贡献。

进行降维。

原文地址:https://www.cnblogs.com/Miracevin/p/10739810.html

时间: 2024-08-02 06:30:11

[CTSC2018]暴力写挂——边分树合并的相关文章

CTSC2018 暴力写挂

CTSC2018 暴力写挂 题意: 题目传送门 题解: emm--第一次写边分治-- 考虑到第二棵树上的\(Lca\)我们难以处理,于是我们可以考虑枚举第二棵树上的\(Lca\),然后在第一棵树上最大化\(dep_u + dep_v - dep_{lca}\).但是在这个式子中,又受到了第一棵树上\(Lca\)的限制,于是我们考虑化简式子.稍微化简一下会发现式子变成了\(\frac{1}{2} * (dep_u + dep_v + dis_{u, v})\),然后我们就可以将其转化成无根树来做了

UOJ#400. 【CTSC2018】暴力写挂 边分治 线段树合并

原文链接 www.cnblogs.com/zhouzhendong/p/UOJ400.html 前言 老年选手没有码力. 题解 先对第一棵树进行边分治,然后,设点 x 到分治中心的距离为 $D[x]$,点 x 在原树上的深度为 $d[x]$,那么 $$d[x]+d[y] - d[LCA(x,y)] - d'[LCA(x,y)] = \frac 12(D[x] + d[x]) + \frac 12 (D[y] + d[y]) - d'[LCA(x,y)]$$ 于是我们考虑将分治区域内的节点在第二棵

Loj #2553. 「CTSC2018」暴力写挂

Loj #2553. 「CTSC2018」暴力写挂 题目描述 temporaryDO 是一个很菜的 OIer .在 4 月,他在省队选拔赛的考场上见到了<林克卡特树>一题,其中 \(k = 0\) 的部分分是求树 \(T\) 上的最长链.可怜的 temporaryDO 并不会做这道题,他在考场上抓猫耳挠猫腮都想不出一点思路. 这时,善良的板板出现在了空中,他的身上发出璀璨却柔和的光芒,荡漾在考场上.''题目并不难.'' 板板说.那充满磁性的声音,让 temporaryDO 全身充满了力量. 他

uoj#400. 【CTSC2018】暴力写挂(边分治)

传送门 做一道题学一堆东西.jpg 猫老师的题--暴力拿的分好像比打挂的正解多很多啊--我纯暴力+部分分已经能有80了--正解没调对之前一直只有10分→_→ 先说一下什么是边分治.这个其实类似于点分治,不过分治对象从点换成边了,就是每次找到一条边,使其断开之后的两个连通块中最大的最小 于是我们就可以--等会儿如果在菊花图上怎么办?不是得卡到\(O(n^2)\)了? 不难发现这个东西的复杂度和节点的度数有关,于是为了假装这个东西能用避免这些情况,我们要把图给重构喽 简单来说就是通过加入虚点,把图给

暴力写挂

题目描述 题解 考虑把式子化一下,因为只有一个式子跟第二棵树有关,所以我们可以考虑把前面的式子化成跟 $\text{lca}$ 没有关系,即 $\frac{1}{2}(dp_u+dp_v+dis(u,v))$ .因此我们可以利用边分治,每次把两边的点黑白染色,构成虚树,然后做 $\text{dp}$ 即可.这里要注意 $\text{lca}$ 要 $O(1)$ 求,虚树构成过程中不能排序,故我们可以一开始就按照第二棵树的dfs排序好,之后分治下去即可.效率: $O(nlogn)$ . 代码 #i

【BZOJ2733】永无乡[splay启发式合并or线段树合并]

题目大意:给你一些点,修改是在在两个点之间连一条无向边,查询时求某个点能走到的点中重要度第k大的点.题目中给定的是每个节点的排名,所以实际上是求第k小:题目求的是编号,不是重要度的排名.我一开始差点被这坑了. 网址:http://www.lydsy.com/JudgeOnline/problem.php?id=2733 这道题似乎挺经典的(至少我看许多神犇很早就做了这道题).这道题有两种写法:并查集+(splay启发式合并or线段树合并).我写的是线段树合并,因为--splay不会打+懒得学.

bzoj 4631: 踩气球 线段树合并

4631: 踩气球 Time Limit: 10 Sec  Memory Limit: 256 MBSubmit: 265  Solved: 136[Submit][Status][Discuss] Description 六一儿童节到了, SHUXK 被迫陪着M个熊孩子玩一个无聊的游戏:有N个盒子从左到右排成一排,第i个盒子里装着Ai个气球. SHUXK 要进行Q次操作,每次从某一个盒子里拿出一个没被踩爆的气球,然后熊孩子们就会立刻把它踩爆. 这M个熊孩子每个人都指定了一个盒子区间[Li, R

[树上差分][线段树合并]JZOJ 3397 雨天的尾巴

Description 深绘里一直很讨厌雨天. 灼热的天气穿透了前半个夏天,后来一场大雨和随之而来的洪水,浇灭了一切. 虽然深绘里家乡的小村落对洪水有着顽固的抵抗力,但也倒了几座老房子,几棵老树被连 根拔起,以及田地里的粮食被弄得一片狼藉. 无奈的深绘里和村民们只好等待救济粮来维生. 不过救济粮的发放方式很特别. 首先村落里的一共有n 座房屋,并形成一个树状结构.然后救济粮分m 次发放,每次选择 两个房屋(x,y),然后对于x 到y 的路径上(含x 和y) 每座房子里发放一袋z 类型的救济粮.

【BZOJ4399】魔法少女LJJ 线段树合并

[BZOJ4399]魔法少女LJJ Description 在森林中见过会动的树,在沙漠中见过会动的仙人掌过后,魔法少女LJJ已经觉得自己见过世界上的所有稀奇古怪的事情了LJJ感叹道“这里真是个迷人的绿色世界,空气清新.淡雅,到处散发着醉人的奶浆味:小猴在枝头悠来荡去,好不自在:各式各样的鲜花争相开放,各种树枝的枝头挂满沉甸甸的野果:鸟儿的歌声婉转动听,小河里飘着落下的花瓣真是人间仙境”SHY觉得LJJ还是太naive,一天,SHY带着自己心爱的图找到LJJ,对LJJ说:“既然你已经见识过动态树