zoukankan      html  css  js  c++  java
  • LOJ#2249 Luogu P2305「NOI2014」购票

    几乎肝了半个下午和整个晚上

    斜率优化的模型好多啊...

    LOJ #2249 Luogu P2305


    题意

    给定一棵树,第$ i$个点如果离某个祖先$ x$的距离不超过$ L_i$,可以花费$ P_i·dist(i,x)+Q_i$的代价跳到点$ x$,

    求每个点走到根的最小代价

    点数不超过$ 2·10^5$


    $ Solution$

    用$dis_x$表示$ x$到根的距离

    首先考虑一条链的情况

    尝试斜率优化

    容易推出两个点$j,k$,若$ dis_k>dis_j且k比j优$当且仅当$ frac{dp_k-dp_j}{dis_k-dis_j}<P_i$

    由于有$ P_i$不具有单调性可以维护单调栈然后每次在里面二分

    但是由于有$ L_i$这个限制,这个单调栈并不容易直接维护

    考虑$ CDQ$分治,即把问题转化成求左半部分对右半部分的转移贡献

    将右半部分的点按照$ dis[i]-L_i$从大到小排序

    每次枚举右边的点,将左边的点按距离从大到小加入单调栈中

    显然现在没有$ L_i$的限制就可以直接在单调栈中二分

    时间复杂度$ O(n log^2   n)$

    然后就是把链上的问题放到树上

    树上分治很容易想到直接点分治

    流程如下:

    1.找到当前子树的重心

    2.将重心及重心外侧的子树递归计算(相当于序列上$CDQ$分治中的$calc(L,mid)$)

    3.计算重心外侧的子树对重心以下的若干棵子树的贡献(相当于序列上$ CDQ$分治中左边对右边的贡献)

    4.递归计算重心以下的各棵子树的答案(相当于序列上$ CDQ$分治中的$calc(mid+1,R)$)

    总复杂度$ O(n log^2   n)$


    $ my code$

    #include<ctime>
    #include<cmath>
    #include<cstdio>
    #include<cstring>
    #include<iostream>
    #include<algorithm>
    #include<queue>
    #define M 200010
    #define rt register int
    #define ll long long
    using namespace std;
    namespace fast_IO{
        const int IN_LEN=10000000,OUT_LEN=10000000;
        char ibuf[IN_LEN],obuf[OUT_LEN],*ih=ibuf+IN_LEN,*oh=obuf,*lastin=ibuf+IN_LEN,*lastout=obuf+OUT_LEN-1;
        inline char getchar_(){return (ih==lastin)&&(lastin=(ih=ibuf)+fread(ibuf,1,IN_LEN,stdin),ih==lastin)?EOF:*ih++;}
        inline void putchar_(const char x){if(oh==lastout)fwrite(obuf,1,oh-obuf,stdout),oh=obuf;*oh++=x;}
        inline void flush(){fwrite(obuf,1,oh-obuf,stdout);}
    }
    using namespace fast_IO;
    #define getchar() getchar_()
    #define putchar(x) putchar_((x))
    inline ll read(){
        ll x = 0; char zf = 1; char ch = getchar();
        while (ch != '-' && !isdigit(ch)) ch = getchar();
        if (ch == '-') zf = -1, ch = getchar();
        while (isdigit(ch)) x = x * 10 + ch - '0', ch = getchar(); return x * zf;
    }
    void write(ll y){if(y<0)putchar('-'),y=-y;if(y>9)write(y/10);putchar(y%10+48);}
    void writeln(const ll y){write(y);putchar('
    ');}
    int i,j,k,m,n,x,y,z,cnt;
    ll dis[M],lim[M],f[M],p[M],q[M]; 
    int F[M],L[M],N[M],a[M],fa[M];
    void add(int x,int y){
        a[++k]=y;
        if(!F[x])F[x]=k;
        else N[L[x]]=k;
        L[x]=k;
    }
    int size[200010],nowmin,all,Q[200010],sta[200010],top,Root,troot;
    bool vis[200010];
    inline bool cmp(int x,int y){return dis[x]-lim[x]>dis[y]-lim[y];}
    inline double slope(int x,int y){return (double)(f[x]-f[y])/(dis[x]-dis[y]);}
    void get(int x){
        size[x]=1;int maxsum=0;
        for(rt i=F[x];i;i=N[i])if(!vis[a[i]]){
            get(a[i]);size[x]+=size[a[i]];
            maxsum=max(maxsum,size[a[i]]);
        }
        maxsum=max(maxsum,all-size[x]+1);
        if(maxsum<nowmin)nowmin=maxsum,Root=x;
    }
    int len;
    void dfs(int x){Q[++len]=x;for(rt i=F[x];i;i=N[i])if(!vis[a[i]])dfs(a[i]);}
    void calc(int x,int sz){
        if(sz<=1)return;
        nowmin=all=sz;get(x);int u=Root;
        for(rt i=F[u];i;i=N[i])vis[a[i]]=1;
        calc(x,sz-size[u]+1);len=0;
        for(rt i=F[u];i;i=N[i])dfs(a[i]);
        sort(Q+1,Q+len+1,cmp);top=0;
        for(rt i=1,pl=u;i<=len;i++){
            while(dis[pl]>=dis[Q[i]]-lim[Q[i]]&&pl!=fa[x]){
                while(top>=2&&slope(pl,sta[top])>slope(sta[top],sta[top-1]))top--;
                sta[++top]=pl;pl=fa[pl];
            }
            f[Q[i]]=min(f[Q[i]],f[sta[top]]+(dis[Q[i]]-dis[sta[top]])*p[Q[i]]+q[Q[i]]);
            if(top<=1)continue;
            int L=1,R=top-1;
            while(L<=R){
                const int mid=L+R>>1;
                if((f[sta[mid]]-f[sta[mid+1]])<=p[Q[i]]*(dis[sta[mid]]-dis[sta[mid+1]]))
                R=mid-1;else L=mid+1;
            }
            f[Q[i]]=min(f[Q[i]],f[sta[L]]+(dis[Q[i]]-dis[sta[L]])*p[Q[i]]+q[Q[i]]);
        }    
        for(rt i=F[u];i;i=N[i])calc(a[i],size[a[i]]);
    }
    int main(){
        n=read();x=read();
        memset(f,10,sizeof(f));f[1]=0;
        for(rt i=2;i<=n;i++){
            fa[i]=read();add(fa[i],i);
            dis[i]=dis[fa[i]]+read();
            p[i]=read();q[i]=read();lim[i]=read();
        }        
        calc(1,n);
        for(rt i=2;i<=n;i++)writeln(f[i]);
        return flush(),0;
    }
  • 相关阅读:
    第一篇正式文章 随便聊聊吧
    CSS 28 块之间的空格
    CSS 27 贴在下方
    CSS 26 左右固定
    CSS 25 垂直居中
    CSS 24 左侧固定
    CSS 23 水平居中
    CSS 22 显示方式
    CSS 21 浮动
    CSS 20 相对定位
  • 原文地址:https://www.cnblogs.com/DreamlessDreams/p/10079414.html
Copyright © 2011-2022 走看看