Description
给你一个长为nnn的序列aaa和一个常数kkk
有mmm次询问,每次查询一个区间[l,r][l,r][l,r]内所有数最少分成多少个连续段,使得每段的和都 ≤k\le k≤k
如果这一次查询无解,输出“Chtholly"“Chtholly"“Chtholly"
Input
第一行三个数n,m,kn,m,kn,m,k
第二行nnn个数表示这个序列aaa
之后mmm行,每行给出两个数l,rl,rl,r表示一次询问
(1≤n,m≤106,1≤ai,k≤109)(1\le n,m\le 10^6,1\le a_i,k\le 10^9)(1≤n,m≤106,1≤ai,k≤109)
Output
输出mmm行,每行一个整数,表示答案
Sample Input
5 5 7
2 3 2 3 4
3 3
4 4
5 5
1 5
2 4
Sample Output
1
1
1
2
2
Solution
为分成尽可能少的段显然要把每一段尽可能扩展,假设第iii个位置开始最多可以扩展到fif_ifi位置,以dp[i][j]dp[i][j]dp[i][j]表示从iii开始扩展2j2^j2j次能够达到的最远距离,那么有转移
dp[i][0]=fi+1,dp[i][j]=dp[dp[i][j−1]][j−1],j>0
dp[i][0]=f_i+1,dp[i][j]=dp[dp[i][j-1]][j-1],j>0
dp[i][0]=fi+1,dp[i][j]=dp[dp[i][j−1]][j−1],j>0
之后对于每组查询,从lll开始倍增跳即可,时间复杂度O(nlog2n)O(nlog_2n)O(nlog2n)
Code
#include<cstdio>
#include<algorithm>
using namespace std;
typedef long long ll;
const int maxn=1000006;
int n,m,k,a[maxn],dp[maxn][21];
ll s[maxn];
int main()
{
scanf("%d%d%d",&n,&m,&k);
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
s[i]=s[i-1]+a[i];
}
for(int i=0;i<=20;i++)dp[n+1][i]=n+1;
for(int i=n;i>=1;i--)
{
dp[i][0]=upper_bound(s+1,s+n+1,s[i-1]+k)-s;
for(int j=1;j<=20;j++)
dp[i][j]=dp[dp[i][j-1]][j-1];
}
while(m--)
{
int l,r,ans=1;
scanf("%d%d",&l,&r);
for(int i=20;i>=0;i--)
if(dp[l][i]<=r)
ans+=(1<<i),l=dp[l][i];
if(dp[l][0]>r)printf("%d\n",ans);
else printf("Chtholly\n");
}
return 0;
}