牛客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\n",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\n",res); } return 0; } int main(){ Ac(); return 0; }
It is your time to fight!