[BZOJ2870]最长道路tree 边分治

因为点分治会带来子树信息合并的问题,于是考虑通过添加虚点的方法把多叉树转成二叉树,然后采用边分治,每次考虑经过分治重边的情况,把两边的所有点按路径上的最小权值排序,然后相互更新一下即可,复杂度 O(nlog2n) O ( n log 2 ⁡ n )
因为多叉树转二叉树的过程中添加了虚点,所以假如 x x y y 的lca是虚点,那么路径 (x,y) ( x , y ) 上原本的那个实点lca的信息就会丢失掉(导致路径上实点数少 1 1 ,并且更新不到lca的点权),可以把点的信息转到边上,就是一条边的虚实由其叶子方向那个点的虚实决定,然后虚点的权值赋为其父亲的即可。
代码:

#include
#include
#include
#include
#include
#define ll long long
#define PB push_back
#define MP make_pair
#define fs first
#define sc second
#define N 100010
using namespace std;
int n,m,tote,con[N],nxt[N<<1],to[N<<1],vis[N],val[N],sz[N],f[N],top[2];
bool ex[N<<1];
ll ans;
pair<int,int> st[2][N];
vector<int> a[N];
void ins(int x,int y,bool b)
{
    tote++;to[tote]=y;ex[tote]=b;
    nxt[tote]=con[x];con[x]=tote;
}
void dfs1(int v,int fa)
{
    for(int p=con[v];p;p=nxt[p])
        if(to[p]!=fa) dfs1(to[p],v),a[v].PB(to[p]);
}
void getroot(int v,int fa,int sum,int &root)
{
    sz[v]=1;
    for(int p=con[v];p;p=nxt[p])
        if(to[p]!=fa&&!vis[p>>1])
        {
            getroot(to[p],v,sum,root);
            f[p>>1]=max(sz[to[p]],sum-sz[to[p]]);
            if(f[root>>1]>f[p>>1]) root=p;
            sz[v]+=sz[to[p]];
        }
}
void dfs2(int v,int fa,int mi,int len,int b)
{
     mi=min(mi,val[v]);
    if(v<=m)st[b][++top[b]]=MP(mi,len);
    for(int p=con[v];p;p=nxt[p])
        if(to[p]!=fa&&!vis[p>>1])
            dfs2(to[p],v,mi,len+ex[p],b);
}
void solve(int x,int sum)
{
    int root=0;getroot(x,0,sum,root);
    if(!root) return ;
    vis[root>>1]=1;
    top[0]=top[1]=0;
    for(int t=0;t<=1;t++)
        dfs2(to[root^t],0,1e9,0,t),sort(st[t]+1,st[t]+top[t]+1);
    for(int t=0;t<=1;t++)
        for(int i=top[t],j=top[t^1]+1,maxl=0;i;i--)
        {
            while(j&&st[t^1][j-1].fs>=st[t][i].fs) j--,maxl=max(maxl,st[t^1][j].sc);
            if(j<=top[t^1]) ans=max(ans,(ll)st[t][i].fs*(st[t][i].sc+maxl+1+ex[root]));
        }
    solve(to[root],sz[to[root]]);
    solve(to[root^1],sum-sz[to[root]]); 
}
int main()
{
    scanf("%d",&n);m=n;
    ans=0;
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&val[i]);
        ans=max(ans,(ll)val[i]);
    }
    for(int i=1;iint x,y;
        scanf("%d%d",&x,&y);
        ins(x,y,1);ins(y,x,1);
    }
    dfs1(1,0);
    tote=1;memset(con,0,sizeof(con));
    for(int i=1;i<=n;i++)
    {
        int t=a[i].size();
        if(t<=2) for(int j=0;jelse
        {
            int p=n;
            for(int j=0;j2)
            {
                a[++n].PB(a[i][j]);val[n]=val[i];
                if(j1) a[n].PB(a[i][j+1]);
            }
            a[i].clear();
            for(int j=p+1;j<=n;j++)
                a[i].PB(j);
            i--;    
        }
    }
    ans=0;f[0]=n;solve(1,n);
    printf("%lld",ans);
    return 0;
}

你可能感兴趣的:(树分治)