zoukankan      html  css  js  c++  java
  • Dinner

    2019.8.22

    2021.5.29


    Description

    一个 \(n\) 个点的环,有点权。将环拆成至多 \(m\) 段,使得权值和最大的段最小。

    Solution

    之前写的博客是假做法,还是太年轻了,打算重写一遍。做过的题都想不起来了,幸好最后还是想到了正解。

    考虑二分答案,关键在于 check。肯定还是要断环成链,但是发现断环之后相邻段不能快速转移,即知道了 \([l,r]\) 的答案,不能快速算出 \([l+1,r+1]\) 的答案。然后之后想了很多假做法……最后还是想到了,二分了最大段长度之后,一个点能扩展的最远位置是确定的,而且可以通过 two-pointer 快速求出来,具体来说就是记一下前缀和,因为时间均为正,所以前缀和是单调的,然后就可以快速得到后继。若从 \(u\) 开始转移,我们只需要判断转移 \(m\) 此后是不是节点编号大于等于 \(u+n\)

    for(int tl=2;tl<=2*n;tl++){
        while(hd<=tl-n) to[hd]=hd+n; 
        while(hd<tl&&S[tl]-S[hd-1]>x) to[hd]=tl;
    }
    

    这样转移就会快一些,复杂度 \(O(nm\log \sum|T|)\) 。然后我们发现转移只会向后指,实际上形成了一个树形关系。转移 \(m\) 次实际上是求 \(m\) 级父亲,所以想到可以树上倍增。于是复杂度 \(O(n\log^2 n)\)

    #include<stdio.h>
    #include<algorithm>
    using namespace std;
    
    inline int read(){
        int x=0,flag=1; char c=getchar();
        while(c<'0'||c>'9'){if(c=='-')flag=0;c=getchar();}
        while(c>='0'&&c<='9'){x=(x<<1)+(x<<3)+c-48;c=getchar();}
        return flag? x:-x;
    }
    
    const int N=1e5+7;
    
    struct E{
        int next,to;
    }e[N];
    int head[N],cnt=0;
    
    inline void add(int id,int to){
        e[++cnt]=(E){head[id],to};
        head[id]=cnt;
    }
    
    int n,m,a[N],top=0,lim,mi,S[N],fa[N][12];
    
    void dfs(int u){
        for(int i=1;i<12;i++)
            fa[u][i]=fa[fa[u][i-1]][i-1];
        for(int i=head[u];i;i=e[i].next)
            fa[e[i].to][0]=u,dfs(e[i].to);
    }
    
    for(int tl=2;tl<=2*n;tl++){
        while(hd<=tl-n) to[hd]=hd+n; 
        while(hd<tl&&S[tl]-S[hd-1]>x) to[hd]=tl;
    }
    
    bool check(int x){
        int hd=1; cnt=0;
        for(int i=1;i<=2*n;i++) head[i]=0;
        for(int tl=2;tl<=2*n;tl++){
            while(hd<=tl-n){add(hd+n,hd);hd++;}
            while(hd<tl&&S[tl]-S[hd-1]>x){add(tl,hd);hd++;}
        }
        while(hd<2*n){add(2*n,hd);hd++;}
        fa[2*n][0]=2*n,dfs(2*n);
        for(int s=1;s<=n;s++){
            int u=s;
            for(int i=11;~i;i--)
                if(m&(1<<i)) u=fa[u][i];
            if(u>=s+n) return 1;
        }
        return 0;
    }
    
    int calc(){
        int lf=mi,rf=lim,ans=lim;
        while(lf<=rf){
            int mid=(lf+rf)>>1;
            if(check(mid)) rf=mid-1,ans=mid;
            else lf=mid+1;
        }
        return ans;
    }
    
    int main(){
        freopen("dinner.in","r",stdin);
        freopen("dinner.out","w",stdout);
        n=read(),m=read();
        for(int i=1;i<=n;i++)
            lim+=(a[i]=a[i+n]=read()),mi=max(a[i],mi);
        for(int i=1;i<=2*n;i++) S[i]=S[i-1]+a[i];
        printf("%d",calc());
    }
    
  • 相关阅读:
    Luogu 4841 城市规划
    Luogu 4721 【模板】分治 FFT
    Luogu 4091 [HEOI2016/TJOI2016]求和
    Luogu 3723 [AH2017/HNOI2017]礼物
    FFT笔记
    Luogu 4900 食堂
    Luogu 4155 [SCOI2015]国旗计划
    Luogu 4069 [SDOI2016]游戏
    Luogu 4254 [JSOI2008]Blue Mary开公司
    Luogu 4251 [SCOI2015]小凸玩矩阵
  • 原文地址:https://www.cnblogs.com/wwlwQWQ/p/11396243.html
Copyright © 2011-2022 走看看