边分治学习笔记(bzoj2870)

如果点分治的话,那个取最小点权操作很难合并,不过树可以点分治,那自然也可以边分治。我们每次找到一条边,使得删掉这条边后的两个连通块中最大的那个的大小尽可能小,然后处理经过这条边的路径信息……
桥豆麻袋,这个复杂度好像不太对啊。
在菊花图上,好像可以轻易变成 O(n) O ( n ) 的啊!!!
于是我们就要把树做一下转化,如果一个点有太多个儿子,我们就建立若干个虚拟点来管理它的儿子们,这样这棵树就变成了二叉树,可以保证 O(nlogn) O ( n l o g n )
灵魂画手litble
我们发现,只要把虚拟点的点权设为原来那个父亲的点权,就不会影响找点权最小值的操作。但是找链条的长度的话,如果两个点的LCA是一个虚拟点的话,它们真实的父亲的贡献就不会被计算。所以我们令真实边的边权为1,虚拟边边权为0,那么一条链的长度就是这条链边权和+1。
这样就好办多了,每次我们找到一条边,使得删掉这条边后的两个连通块中最大的那个的大小尽可能小,然后把边两端的路径存下来,按照点权最小值排序,然后对于A端的一条路径,我们维护B端点权大于等于该路径的路径中,长度最长的一条,对B端也这么做一边就可以得到答案。复杂度 O(nlog2n) O ( n l o g 2 n )
至于空间,我们知道线段树要开四倍空间,所以对于菊花图,这个重建树就也是四倍空间。

#include<bits/stdc++.h>
using namespace std;
#define RI register int
int read() {
    int q=0;char ch=' ';
    while(ch<'0'||ch>'9') ch=getchar();
    while(ch>='0'&&ch<='9') q=q*10+ch-'0',ch=getchar();
    return q;
}
typedef long long LL;
const int N=200005,inf=0x3f3f3f3f;
int n,kn,tot,rt,mxx;LL ans;
int v[N],sz[N],h[N],ne[N<<1],to[N<<1],d[N],vis[N],js[2];
struct node{int v,len;}t[2][N];
bool cmp(node x,node y) {return x.v>y.v;}
vector<int> a[N];
void add(int x,int y,int z) {to[++tot]=y,ne[tot]=h[x],h[x]=tot,d[tot]=z;}
void dfs1(int x,int las) {
    for(RI i=h[x];i;i=ne[i])
        if(to[i]!=las) a[x].push_back(to[i]),dfs1(to[i],x);
}
void rebuild() {//重新建树
    tot=1;for(RI i=1;i<=n;++i) h[i]=0;
    for(RI i=1;i<=n;++i) {
        int sz=a[i].size();
        if(sz<=2) {
            for(RI j=0;j<sz;++j)
                add(i,a[i][j],(a[i][j]<=kn)),add(a[i][j],i,(a[i][j]<=kn));
        }
        else {
            int o1=++n,o2=++n;v[o1]=v[o2]=v[i];
            add(i,o1,0),add(o1,i,0),add(i,o2,0),add(o2,i,0);
            for(RI j=0;j<sz;++j)
                if(j&1) a[o2].push_back(a[i][j]);
                else a[o1].push_back(a[i][j]);
        }
    }
}
void getrt(int x,int las,int SZ) {
    sz[x]=1;
    for(RI i=h[x];i;i=ne[i]) {
        if(vis[i>>1]||to[i]==las) continue;
        getrt(to[i],x,SZ),sz[x]+=sz[to[i]];
        int kl=max(sz[to[i]],SZ-sz[to[i]]);
        if(kl<mxx) mxx=kl,rt=i;
    }
}
void dfs2(int o,int x,int las,int len,int val) {
    val=min(val,v[x]),t[o][++js[o]]=(node){val,len};
    for(RI i=h[x];i;i=ne[i]) {
        if(vis[i>>1]||to[i]==las) continue;
        dfs2(o,to[i],x,len+d[i],val);
    }
}
void work(int x,int SZ) {
    mxx=inf,getrt(x,0,SZ);
    if(mxx==inf) return;
    int now=rt;vis[now>>1]=1;
    js[0]=js[1]=0,dfs2(0,to[now],0,0,inf),dfs2(1,to[now^1],0,0,inf);
    sort(t[0]+1,t[0]+js[0]+1,cmp),sort(t[1]+1,t[1]+js[1]+1,cmp);
    for(RI i=1,j=1,mxl=0;i<=js[0];++i) {
        while(j<=js[1]&&t[1][j].v>=t[0][i].v) mxl=max(mxl,t[1][j].len),++j;
        if(j!=1) ans=max(ans,1LL*t[0][i].v*(mxl+t[0][i].len+d[now]+1));
    }
    for(RI i=1,j=1,mxl=0;i<=js[1];++i) {
        while(j<=js[0]&&t[0][j].v>=t[1][i].v) mxl=max(mxl,t[0][j].len),++j;
        if(j!=1) ans=max(ans,1LL*t[1][i].v*(mxl+t[1][i].len+d[now]+1));
    }
    int ksz=sz[to[now]];work(to[now],ksz),work(to[now^1],SZ-ksz);
}
int main()
{
    int x,y;
    n=kn=read();
    for(RI i=1;i<=n;++i) v[i]=read();
    for(RI i=1;i<n;++i) x=read(),y=read(),add(x,y,1),add(y,x,1);
    dfs1(1,0),rebuild(),work(1,n);
    printf("%lld",ans);
    return 0;
}
  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值