牛客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;
}
原文地址:https://www.cnblogs.com/hua-dong/p/10820908.html