• 牛客82-B:区间的连续段 (ST表,贪心)(WXK牛逼)


    题目描述

    给你一个长为n的序列a和一个常数k

    有m次询问,每次查询一个区间[l,r]内所有数最少分成多少个连续段,使得每段的和都 <= k

    如果这一次查询无解,输出"Chtholly"

    输入描述:

    第一行三个数n,m,k
    第二行n个数表示这个序列a
    之后m行,每行给出两个数l r表示一次询问

    输出描述:

    输出m行,每行一个整数,表示答案

    输入

    5 5 7
    2 3 2 3 4
    3 3
    4 4
    5 5
    1 5
    2 4

    输出

    1
    1
    1
    2
    2

    sol:初看以为是线段树题,但是肯定会被卡。   我们用st表预处理,st[i][j]表示i点右移1<<j刀的最远距离。  和求LCA的道理一样,只要没到达边界,贪就完事了。

    #include<bits/stdc++.h>
    #define ll long long
    #define rep(i,a,b) for(int i=a;i<=b;i++)
    using namespace std;
    const int maxn=1000010;
    ll a[maxn]; int st[maxn][21],lg[maxn];
    int main()
    {
        int N,M,K,L,R,pos,res;
        scanf("%d%d%d",&N,&M,&K);
        rep(i,1,N) lg[i]=lg[i>>1]+1;
        rep(i,1,N) scanf("%lld",&a[i]);
        rep(i,1,N) a[i]+=a[i-1];
        rep(i,0,20) st[N+1][i]=N+1;
        for(int i=N;i>=1;i--){
            pos=upper_bound(a+1,a+N+1,a[i-1]+K)-a;
            st[i][0]=pos;
            rep(j,1,20) st[i][j]=st[st[i][j-1]][j-1];
        }
        rep(i,1,M){
            scanf("%d%d",&L,&R);
            pos=L;  res=0;
            for(int j=lg[R-L+1];j>=0;j--) {
                if(st[pos][j]<=R) pos=st[pos][j],res+=(1<<j);
            }
            if(st[pos][0]<=R) puts("Chtholly");
            else printf("%d
    ",res+1);
        }
        return 0;
    }

    来自WXK更加NB的做法,目前时间上RK1。

    先不考虑K的限制:对于每个点L,我们找到右边界R,[L,R),连边L->R,表示从L出发,最多走到R-1,下一步的起点是R。 然后倒着建树,父亲唯一的,对应的是下一步。   得到dep,得到dfs序。  那么我们dep做差就能逼近答案。   (边--对应了区间划分)

    dep做差的前提是我们选择的参照物一致,都是树根,但并非询问的L或者R,会导致答案可能多1,所以我们应该靠齐参照物L,那么我们还要找到L对应到dep[R]的那一层的祖先(这里保存了每一层的节点,利用dfs序找),看它的位置是否包括了R,不包括的话,答案-1。

    (由于K的存在,实际操作的是一个森林。

    (道理就是一个尺子,量长度,向下取整。 如果我以[0,1]作为起点,答案可能+1,可能是正确的,逼近了这个答案后去验证。

    #include<bits/stdc++.h>
    #define rep(i,a,b) for(int i=a;i<=b;i++)
    #define ll long long
    using namespace std;
    const int maxn=1e6+10;
    int a[maxn],dep[maxn],dfn[maxn],To[maxn];
    int pre[maxn],tot; ll sum[maxn];
    vector<int>G[maxn],C[maxn]; //G保存树,C保存每一层。
    void dfs(int u,int f){
        dep[u]=dep[f]+1; dfn[u]=++tot; To[tot]=u;
        C[dep[u]].push_back(tot);
        for(int i=0;i<G[u].size();i++) dfs(G[u][i],u);
    }
    int Ac(){
        int N,Q,K,L,R;
        scanf("%d%d%d",&N,&Q,&K);
        rep(i,1,N) scanf("%d",&a[i]); a[N+1]=K+1;
        rep(i,1,N+1) pre[i]=pre[i-1]+(a[i]>K), sum[i]=sum[i-1]+a[i];
        int now=N+1;
        for(int i=N;i>=1;i--){
            if(a[i]>K) { now=i; continue;}
            int pos=upper_bound(sum+1,sum+N+1,sum[i-1]+K)-sum;
            pos=min(pos,now); G[pos].push_back(i);
        }
        for(int i=N+1;i>=1;i--) if(a[i]>K) dfs(i,i+1);
        while(Q--){
            scanf("%d%d", &L, &R);
            if(pre[R]-pre[L-1]!=0){
                puts("Chtholly");
                continue;
            }
            int res=dep[L]-dep[R]+1;
            int pos=upper_bound(C[dep[R]].begin(),C[dep[R]].end(),dfn[L])-C[dep[R]].begin();
            if(To[C[dep[R]][pos-1]]>R) res--;
            printf("%d
    ",res);
        }
        return 0;
    }
    int main(){
        Ac();
        return 0;
    }
  • 相关阅读:
    windows中administrator 和 administrators两个账户的区别
    如何去掉打印网页时自带的网址以及页码等内容
    Oracle左连接,右连接
    oracle服务器本地能够登录但是局域网内其他机器不能访问的解决方法
    错误Name node is in safe mode的解决方法
    oracle数据库中对varchar类型求max的解决方法
    JBoss中配置数据源出现错误:“Failed to register driver for: com.mysql.jdbc.Driver”的解决方法
    学习junit和hamcrest的使用
    Ubuntu10.10如何给用户添加sudo权限
    ORACLE 9i卸载并重新安装
  • 原文地址:https://www.cnblogs.com/hua-dong/p/10820908.html
Copyright © 2020-2023  润新知