[WC2018]通道

 [WC2018]通道 

爽到爽到爽到爽到爽到爽到爽到爽到爽到爽到爽到爽到爽到一遍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('\n');}

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
*/
View Code

 

 

 

一句话总结就是:

以“第一棵树跨越中心边LR集合、第二棵树虚树LCA处”为标准,利用“备选集合在第三棵树上最大值”的一个性质,进行统计。

 

posted @ 2019-03-29 16:26  *Miracle*  阅读(669)  评论(0编辑  收藏  举报