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

来源:互联网 发布:截面数据统计分析方法 编辑:程序博客网 时间:2024/05/20 13:36

传送门
似乎这题有很多种做法。
我的做法是, 先二分答案,然后再分治,这样,如果一开始就找到了路,就能很快返回。然而点分时找重心的常数比较大,我又是每次重新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;}
阅读全文
0 0