爽到爽到爽到爽到爽到爽到爽到爽到爽到爽到爽到爽到爽到一遍AC
但是题目太神主要还是为了锻炼码力
3棵树,考虑层层处理,互相考虑
第一棵
边分治。儿子少。虚边权值为0。
统计在第一棵树上跨过中心边的点对
找到d1,到中心边距离。两个点第一个距离已经可以计算
左部为L,右部为R。
下面以这个为基础统计
第二棵
第二棵第三棵的结构无法掌握。要具体一些。
所以虚树,对于当前边分治的点集建第二棵树上的虚树
第二棵树上两点距离?dis+dis-lca!
枚举LCA,统计一个属于L的,一个属于R的,并且第二棵树上的虚树LCA是x的点对
已经可以做两棵树的了。
第三棵
虚树合并的时候,本质是考虑两个子树的备选集合进行合并,但是取max,所以我们只保留了最大的。
我们直接考虑dis是怎么来的:V+d1[x]+d1[y]+d2[x]+d2[y]-2*d2[lca(x,y)]+dis3(x,y)
2*d2[lca(x,y)]我们正在枚举lca,V,分治树中心边权值是定值
所以,我们要从L中选择x,R中选择y,最大化(d1[x]+d2[x])+(d1[y]+d2[y])+dis3(x,y)
记录一个没有最优子结构了。所以考虑记录集合
树形DP本质是集合合并,如果我们知道合并后的集合放在T3上的最大值,就可以更新答案
集合当然是无法记录的,但是我们只关心“(d1[x]+d2[x])+(d1[y]+d2[y])+dis3(x,y)”的最大值
结论:
都是正边权正权点,两个集合合并之后,dis的最大值,就是原来四个端点两两最大值
所以L或者R的备选集合记录最长dis的端点,先L,R或者R,L贡献答案,再从儿子合并L或者R
开f[x][1/2],记录来自L,R子集的集合的端点
然后没了
代码
细节:
1.LCA用ST表O(1)做,是欧拉遍历序,预处理时候注意i+(1<<j)-1<=lim
2.多开namespace。。。
#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(' ');} namespace Miracle{ const int N=2e5+5; int n;int lg[2*N];int mem[N],num;//set of L and R ll ans;ll d1[N],d2[N],d3[N]; int be[N];//L : 1 or R : 2 ll V; namespace t3{ struct node{ int nxt,to;ll w; }e[2*N]; int hd[N],cnt; void add(int x,int y,ll z){e[++cnt].nxt=hd[x];e[cnt].w=z;e[cnt].to=y;hd[x]=cnt;} int dfn[N],dfn2[N];int dep[N],g[2*N][20];int id[2*N],df; void dfs(int x,int fa,int d){ id[++df]=x;dep[x]=d; dfn[x]=df; for(reg i=hd[x];i;i=e[i].nxt){ int y=e[i].to;if(y==fa)continue; d3[y]=d3[x]+e[i].w; dfs(y,x,d+1);id[++df]=x; } dfn2[x]=df; } int lca(int x,int y){ if(dfn[x]>dfn[y]) swap(x,y);int len=dfn[y]-dfn[x]+1;len=lg[len];x=dfn[x],y=dfn[y];//warning!! if(dep[g[x][len]]<dep[g[y-(1<<len)+1][len]]) return g[x][len];return g[y-(1<<len)+1][len]; } ll dis(int x,int y){ return d1[x]+d2[x]+d1[y]+d2[y]+d3[x]+d3[y]-1LL*2*d3[lca(x,y)]; } void build(){ int x,y;ll z; for(reg i=1;i<n;++i) rd(x),rd(y),rd(z),add(x,y,z),add(y,x,z); dfs(1,0,1); for(reg i=1;i<=df;++i) g[i][0]=id[i]; for(reg j=1;j<=18;++j){ for(reg i=1;(i+(1<<j)-1)<=df;++i){ if(dep[g[i][j-1]]<dep[g[i+(1<<(j-1))][j-1]]) g[i][j]=g[i][j-1]; else g[i][j]=g[i+(1<<(j-1))][j-1]; } } } } ///////////////////////////////////////////////////////////////////////////////////////////T3T3T3T3T3T3 namespace t2{ struct node{ int nxt,to;ll w; }e[2*N]; int hd[N],cnt; void add(int x,int y,ll z){e[++cnt].nxt=hd[x];e[cnt].w=z;e[cnt].to=y;hd[x]=cnt;} int dfn[N],dfn2[N];int dep[N],g[2*N][20];int id[2*N],df; void dfs(int x,int fa,int d){ id[++df]=x;dep[x]=d; dfn[x]=df; for(reg i=hd[x];i;i=e[i].nxt){ int y=e[i].to;if(y==fa)continue; d2[y]=d2[x]+e[i].w; dfs(y,x,d+1);id[++df]=x; } dfn2[x]=df; } int lca(int x,int y){ if(dfn[x]>dfn[y]) swap(x,y);int len=dfn[y]-dfn[x]+1;len=lg[len];x=dfn[x],y=dfn[y];//warning!! if(dep[g[x][len]]<dep[g[y-(1<<len)+1][len]]) return g[x][len];return g[y-(1<<len)+1][len]; } void build(){ int x,y;ll z; for(reg i=1;i<n;++i) rd(x),rd(y),rd(z),add(x,y,z),add(y,x,z); dfs(1,0,1); for(reg i=1;i<=df;++i) g[i][0]=id[i]; for(reg j=1;j<=18;++j){ for(reg i=1;(i+(1<<j)-1)<=df;++i){ if(dep[g[i][j-1]]<dep[g[i+(1<<(j-1))][j-1]]) g[i][j]=g[i][j-1]; else g[i][j]=g[i+(1<<(j-1))][j-1]; } } memset(hd,0,sizeof hd);//warning!!! cnt=0;//warning!!! } int sta[N],top; pair<int,int>f[N][3]; int tmp[10]; ll calc(pair<int,int>A,pair<int,int>B){//for ans if(A.fi==0&&A.se==0) return 0;//empty else if(B.fi==0&&B.se==0) return 0; return max(t3::dis(A.fi,B.fi),max(t3::dis(A.se,B.fi),max(t3::dis(A.fi,B.se),t3::dis(A.se,B.se)))); } pair<int,int>merge(pair<int,int>A,pair<int,int>B){ if(A.fi==0&&A.se==0) return B;//empty else if(B.fi==0&&B.se==0) return A; int lp=0; pair<int,int>ret; ll mx=-233; tmp[++lp]=A.fi,tmp[++lp]=A.se; tmp[++lp]=B.fi,tmp[++lp]=B.se; for(reg i=1;i<=lp-1;++i){ for(reg j=i+1;j<=lp;++j){ ll now=t3::dis(tmp[i],tmp[j]); if(now>mx){ mx=now;ret.fi=tmp[i];ret.se=tmp[j]; } } } return ret; } void dp(int x){//first add x if(be[x]){ f[x][be[x]].fi=x;f[x][be[x]].se=x; } for(reg i=hd[x];i;i=e[i].nxt){ int y=e[i].to; dp(y); ans=max(ans,max(calc(f[x][1],f[y][2]),calc(f[x][2],f[y][1]))-1LL*d2[x]*2+V); f[x][1]=merge(f[x][1],f[y][1]); f[x][2]=merge(f[x][2],f[y][2]); } } bool cmp(int x,int y){ return dfn[x]<dfn[y]; } void wrk(){//build xushu and DP sort(mem+1,mem+num+1,cmp); int lp=num; for(reg i=1;i<lp;++i){ mem[++num]=lca(mem[i],mem[i+1]); } sort(mem+1,mem+num+1,cmp); num=unique(mem+1,mem+num+1)-mem-1; top=0; for(reg i=1;i<=num;++i){ while(top&&(!(dfn2[sta[top]]>=dfn2[mem[i]]))) --top; if(top) add(sta[top],mem[i],0); sta[++top]=mem[i]; } dp(mem[1]); //clear!!!! for(reg i=1;i<=num;++i) { int x=mem[i]; be[x]=0,hd[x]=0,d1[x]=0,f[x][1].fi=0,f[x][1].se=0; f[x][2].fi=0;f[x][2].se=0; } top=0;cnt=0; } } ////////////////////////////////////////////////////////////////////////////////////////T2T2T2T2T2T2T2T2T2T2T2T2 namespace t1{ struct node{ int nxt,fr,to;ll w; }e[2*N]; int hd[N],cnt=1; bool vis[2*N]; void add(int x,int y,ll z){e[++cnt].nxt=hd[x];e[cnt].w=z;e[cnt].to=y;e[cnt].fr=x;hd[x]=cnt;} vector<pair<int,ll> >to[N]; bool exi[N];//is or not true point int cur; void rebuild(int x,int fa){ exi[x]=1;int las=0; for(reg i=0;i<(int)to[x].size();++i){ int y=to[x][i].fi; if(y==fa) continue; if(!las){ add(x,y,to[x][i].se); add(y,x,to[x][i].se); las=x; }else{ ++cur;add(las,cur,0);add(cur,las,0); add(cur,y,to[x][i].se);add(y,cur,to[x][i].se); las=cur; } rebuild(y,x); } } int nowsz,rt; int mi; int sz[N]; void dfs(int x,int fa){ sz[x]=1; for(reg i=hd[x];i;i=e[i].nxt){ int y=e[i].to; if(y==fa||vis[i]) continue; dfs(y,x);sz[x]+=sz[y]; if(max(sz[y],nowsz-sz[y])<mi){ mi=max(sz[y],nowsz-sz[y]); rt=i; } } } void dfs2(int x,int fa,int typ){//exi!!! sz[x]=1; if(exi[x]) mem[++num]=x,be[x]=typ; for(reg i=hd[x];i;i=e[i].nxt){ int y=e[i].to; if(y==fa||vis[i]) continue; d1[y]=d1[x]+e[i].w; dfs2(y,x,typ); sz[x]+=sz[y]; } } void divi(int x){ if(nowsz==1) return; rt=0; mi=0x3f3f3f3f; dfs(x,0); int L=e[rt].fr,R=e[rt].to;//L is R 's fa vis[rt]=vis[rt^1]=1; num=0; d1[L]=0,d1[R]=0; dfs2(L,R,1);dfs2(R,L,2); V=e[rt].w; t2::wrk(); nowsz=nowsz-sz[R]; divi(L); nowsz=sz[R]; divi(R); } void build(){ int x,y;ll z; for(reg i=1;i<n;++i){// ,add(x,y,z),add(y,x,z); rd(x),rd(y),rd(z);to[x].push_back(mk(y,z)); to[y].push_back(mk(x,z)); } cur=n; rebuild(1,0); } void sol(){ nowsz=cur; divi(1); } } int main(){ rd(n);lg[0]=0; for(reg i=1;i<=2*n;++i) lg[i]=(i>>(lg[i-1]+1))?lg[i-1]+1:lg[i-1]; t1::build(); t2::build(); t3::build(); t1::sol();printf("%lld",ans);return 0; } } signed main(){ Miracle::main(); return 0; } /* Author: *Miracle* Date: 2019/3/29 11:09:48 */
一句话总结就是:
以“第一棵树跨越中心边LR集合、第二棵树虚树LCA处”为标准,利用“备选集合在第三棵树上最大值”的一个性质,进行统计。