bzoj 5341: [Ctsc2018]暴力写挂
2018-08-04 19:40
239 查看
Description
Solution
边分治+边分树合并这个题很多做法都是启发式合并的复杂度的,都有点卡
以前有个套路叫做线段树合并优化启发式合并,消掉一个 \(log\)
这个题思路类似,建出边分树,通过一些操作把它变成线段树,就可以线段树合并了
首先边分树的相关定理:
如果一棵包含 \(N\) 个结点的树中每个点的度均不大于 \(D\),那么存在一条边,使得分出的两棵子树的结点个数在 \([N/(D+1),N*D/(D+1)]\)
那么边分树的深度和度数是相关的,我们只需要通过加虚点把这棵树变成二叉树就好了
回到这个题:
枚举第二棵树的 \(lca\) , 然后剩下的就是在第二棵树中选出在第一棵树中 \(dep[x]+dep[y]-dep[lca(x,y)]\) 的最大值
我们发现这个要求的东西就是 \(x,y\) 到根的路径的交,所以可以不考虑第一棵树的 \(lca\) 了
其中 \(x,y\) 都来自这个 \(lca\) 的不同子树内
每次插入一个点就像线段树那样更新就行了,不同的是你不能准确定位到这个叶子节点,你需要在 \(build\) 的时候顺便存一下对应关系
枚举第二棵树的 \(lca\) 再合并子树的边分树,顺便更新一下答案就行了
#include<bits/stdc++.h> #define vc vector<edge>::iterator #define pb push_back using namespace std; typedef long long ll; const int N=750000,M=8510000,T=N*2; int n,V,sz ,sum,son ={N},val[T],ls[T],rs[T],dep ,fa[T],tt=0; ll f [20],dis ,fl[M],fr[M],ans=0,D;int lm[M],rm[M],rt[N/2],id[M]; struct edge{ int x,v; edge(){} edge(int _x,int _v){x=_x;v=_v;} }q ; vector<edge>G1 ,G2[N/2]; inline void add(int x,int y,int z,vector<edge>*G){ G[x].pb(edge(y,z));G[y].pb(edge(x,z)); } inline void build(int x,int last){ for(vc it=G1[x].begin();it!=G1[x].end();++it){ if(it->x==last)continue; dis[it->x]=dis[x]+it->v;build(it->x,x); } int l=1,r=0;edge u,v; for(vc it=G1[x].begin();it!=G1[x].end();++it)if(it->x!=last)q[++r]=*it; while(l+2<=r){ int o=++V;u=q[l++],v=q[l++]; add(o,u.x,u.v,G1);add(o,v.x,v.v,G1); q[++r]=edge(o,0); } vector<edge>().swap(G1[x]); while(l<=r)add(x,q[l].x,q[l].v,G1),l++; } inline void getdis(int x,int last,int d){ for(vc it=G1[x].begin();it!=G1[x].end();++it){ if(it->x==last || it->x==-1)continue; f[it->x][d]=f[x][d]+it->v;getdis(it->x,x,d); } } inline void getedge(int x,int last,int &ex,int &ey){ sz[x]=1; for(int i=G1[x].size()-1,u;i>=0;i--){ if((u=G1[x][i].x)==last || u==-1)continue; getedge(u,x,ex,ey);sz[x]+=sz[u]; if(son[u]<son[ey])ex=x,ey=u; } son[x]=abs(sum-2*sz[x]); } inline int solve(int x,int S,int d){ if(S==1){dep[x]=d;return x;} int ex=0,ey=0,o=++V; getdis(x,x,d); sum=S;getedge(x,x,ex,ey); for(vc it=G1[ex].begin();it!=G1[ex].end();++it) if(it->x==ey){val[o]=it->v;it->x=-1;break;} for(vc it=G1[ey].begin();it!=G1[ey].end();++it) if(it->x==ex){it->x=-1;break;} fa[ls[o]=solve(ex,S-sz[ey],d+1)]=o; fa[rs[o]=solve(ey,sz[ey],d+1)]=o; return o; } inline int ins(int x){ for(int i=dep[x],u=x,la=x;i>=1;i--){ id[++tt]=fa[x];fl[tt]=fr[tt]=-1ll<<60; if(ls[fa[x]]==x)fl[tt]=max(fl[tt],dis[u]+f[u][i]),lm[tt]=la; if(rs[fa[x]]==x)fr[tt]=max(fr[tt],dis[u]+f[u][i]),rm[tt]=la; la=tt;x=fa[x]; } return tt; } inline int merge(int x,int y){ if(!x||!y)return x+y; ans=max(ans,(fl[x]+fr[y]+val[id[x]])/2-D); ans=max(ans,(fl[y]+fr[x]+val[id[x]])/2-D); fl[x]=max(fl[x],fl[y]);fr[x]=max(fr[x],fr[y]); lm[x]=merge(lm[x],lm[y]);rm[x]=merge(rm[x],rm[y]); return x; } inline void dfs(int x,int last,ll d){ rt[x]=ins(x);ans=max(ans,dis[x]-d); for(vc it=G2[x].begin();it!=G2[x].end();++it){ if(it->x==last)continue; dfs(it->x,x,d+it->v);D=d; rt[x]=merge(rt[x],rt[it->x]); } } int main(){ freopen("pp.in","r",stdin); freopen("pp.out","w",stdout); cin>>n;V=n; int x,y,z; for(int i=1;i<n;i++)scanf("%d%d%d",&x,&y,&z),add(x,y,z,G1); for(int i=1;i<n;i++)scanf("%d%d%d",&x,&y,&z),add(x,y,z,G2); build(1,1);solve(1,V,0);dfs(1,1,0); cout<<ans<<endl; return 0; }
相关文章推荐
- bzoj 5340: [Ctsc2018]假面
- BZOJ5340 & 洛谷4564 & LOJ2552:[CTSC2018]假面——题解
- BZOJ_5343_[Ctsc2018]混合果汁_二分答案+主席树
- BZOJ5343 [Ctsc2018]混合果汁 【二分 + 主席树】
- BZOJ5343 & 洛谷4602 & LOJ2555:[CTSC2018]混合果汁——题解
- BZOJ5340 [Ctsc2018]假面 【概率dp】
- 【BZOJ】2018: [Usaco2009 Nov]农场技艺大赛(暴力)
- 【BZOJ 1150】 [CTSC2007]数据备份Backup
- [BZOJ5334][TJOI2018]数学计算(exgcd/线段树)
- bzoj2718/1143 CTSC2008 祭祀 最长反链
- 【BZOJ3208】 花神的秒题计划Ⅰ 纯暴力,说好的省选难度呢?
- 【bzoj2806】 Ctsc2012—Cheat
- BZOJ 4397: [Usaco2015 dec]Breed Counting|暴力
- BZOJ-3110-K大数查询-ZJOI2013-暴力
- [bzoj1143][CTSC2008]祭祀
- 【BZOJ】1295: [SCOI2009]最长距离(spfa+暴力)
- [BZOJ2754] 喵星球上的点名 - AC自动机/后缀数组/后缀自动机/玄学♂暴力
- 【BZOJ 1146】[CTSC2008]网络管理Network
- 【bzoj 十连测】[noip2016第二场]Problem A. Divisors(暴力)
- BZOJ 1143 [CTSC2008]祭祀river(二分图匹配)