bzoj1758重建计划,树的点分治+单调队列(可能是常见套路?)

本文介绍了一种结合二分及分治策略解决特定问题的方法。通过先进行二分搜索缩小解的空间,再利用分治法高效处理剩余部分。特别讨论了如何优化点分过程中的重心查找,并详细阐述了实现过程中遇到的问题及其解决方案。

传送门
似乎这题有很多种做法。
我的做法是, 先二分答案,然后再分治,这样,如果一开始就找到了路,就能很快返回。然而点分时找重心的常数比较大,我又是每次重新dfs,而不是再次利用前面的结果,然后就跑得很慢。
分治时,我是将每课子树,每个(不带权的)深度中取出(带权深度)最大的一个,组成一个数组a,先求解,把前面的结果组成一个单调队列,用双指针扫,再和前面的暴力合并,处理时我按子树深度排了个序,从小到大处理,这样,每次合并时,操作次数为所有子树的深度和,然后所有子树的深度和<=所有子树的大小= O(n) ,这样,每次点分的复杂度就是 O(nlogn)
假如不按深度从小到大处理,而是任意顺序,那么对于一条链,链的一端是一朵菊花,处理分治中心为菊花花心时就会爆炸。

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cctype> 
using namespace std;
typedef long long ll;
typedef double db;
const int N=100005;
const db eps=1e-4;
inline void up(db&a,db b){
    if(a<b)a=b;
}
inline void up(int&a,int b){
    if(a<b)a=b;
}
inline int min(int a,int b){
    return a>b?b:a;
}
int n,u,v,w,i,L,U;
db l,r,m;
struct tree{
    struct edge{
        int to,next,w;
        db c;
    }e[N<<1];
    int xb,h[N],rt,sum,sz[N],f[N],w,dd,z,q[N],tt,ww;
    db a[N],m[N],len[N];
    bool b[N];
    struct node{
        int v,l,r;
        bool operator<(const node&x)const{
            return v<x.v;
        }
    }d[N];
    inline void addedge(int u,int v,int w){
        e[++xb]=(edge){v,h[u],w};
        h[u]=xb;
        e[++xb]=(edge){u,h[v],w};
        h[v]=xb;
    }
    void dfs(int x,int fa){
        f[x]=sz[x]=1;
        for(int i=h[x];i;i=e[i].next)
            if(e[i].to!=fa && !b[e[i].to])dfs(e[i].to,x),sz[x]+=sz[e[i].to],up(f[x],sz[e[i].to]);
        up(f[x],sum-sz[x]);
        if(f[rt]>f[x])rt=x;
    }
    void getdep(int x,int fa,int dep){
        if(dep>dd)dd=dep;
        if(len[x]>a[z+dep])a[z+dep]=len[x];
        for(int i=h[x];i;i=e[i].next)
            if(e[i].to!=fa && !b[e[i].to])len[e[i].to]=len[x]+e[i].c,getdep(e[i].to,x,dep+1);
    }
    bool work(int x){
        int i,j;
        for(i=0;i<=sum;++i)m[i]=-(1ll<<50),a[i]=-(1ll<<50);
        w=z=0;
        for(i=h[x];i;i=e[i].next)
            if(!b[e[i].to]){
                len[e[i].to]=e[i].c;
                dd=0;
                getdep(e[i].to,x,1);
                d[++w].v=dd;
                d[w].l=z+1;
                d[w].r=z+=dd;
                for(j=d[w].l+L-1;j<=z;++j)if(a[j]>0)return 1;
            }
        sort(d+1,d+w+1);
        for(j=d[1].l;j<=d[1].r;++j)m[j-d[1].l+1]=a[j];
        for(i=2;i<=w;++i){
            tt=1;
            ww=0;
            for(j=min(d[i-1].r-d[i-1].l+1,U-1);j>=L-1;--j){
                while(ww && m[j]>m[q[ww]])--ww;
                if(!ww || m[j]>m[q[ww]])q[++ww]=j;
            }
            for(j=d[i].l;j<=d[i].r;++j){
                if(j-d[i].l+1+q[tt]>U)++tt;
                if(tt<=ww && m[q[tt]]+a[j]>0)return 1;
                while(tt<=ww && m[L-(j-d[i].l+1)-1]>m[q[ww]])--ww;
                if(tt>ww || m[L-(j-d[i].l+1)-1]>m[q[ww]])q[++ww]=L-(j-d[i].l+1)-1;
            }
            for(j=d[i].l;j<=d[i].r;++j)up(m[j-d[i].l+1],a[j]);
        }
        return 0;
    }
    bool solve(int x){
        b[x]=1;
        for(int i=h[x];i;i=e[i].next)if(sz[e[i].to]>sz[x])sz[e[i].to]=sum-sz[x];
        if(work(x))return 1;
        for(int i=h[x];i;i=e[i].next)
            if(!b[e[i].to]){
                sum=sz[e[i].to];
                rt=0;
                dfs(e[i].to,x);
                if(solve(rt))return 1;
            }
        return 0;
    }
}t;
inline int getint(){
    int x=0;
    char c=getchar();
    while(!isdigit(c))c=getchar();
    for(;isdigit(c);c=getchar())x=x*10+c-48;
    return x;
}
int main(){
    scanf("%d%d%d",&n,&L,&U);
    for(i=1;i<n;++i){
        scanf("%d%d%d",&u,&v,&w);
        t.addedge(u,v,w);
        if(w>r)r=w;
    }
    t.f[0]=1<<30;
    l=0;
    while(r-l>eps){
        m=(l+r)/2;
        memset(t.b,0,sizeof t.b);
        t.sum=n;
        for(i=1;i<=t.xb;++i)t.e[i].c=t.e[i].w-m;
        t.dfs(1,t.rt=0);
        if(t.solve(t.rt))l=m;
            else r=m;
    }
    printf("%.3f\n",l);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值