Description
给你一个长为 n n n的序列 a a a和一个常数 k k k
有 m m m次询问,每次查询一个区间 [ l , r ] [l,r] [l,r]内所有数最少分成多少个连续段,使得每段的和都 ≤ k \le k ≤k
如果这一次查询无解,输出 “ C h t h o l l y " “Chtholly" “Chtholly"
Input
第一行三个数 n , m , k n,m,k n,m,k
第二行 n n n个数表示这个序列 a a a
之后 m m m行,每行给出两个数 l , r l,r l,r表示一次询问
( 1 ≤ n , m ≤ 1 0 6 , 1 ≤ a i , k ≤ 1 0 9 ) (1\le n,m\le 10^6,1\le a_i,k\le 10^9) (1≤n,m≤106,1≤ai,k≤109)
Output
输出 m m m行,每行一个整数,表示答案
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
为分成尽可能少的段显然要把每一段尽可能扩展,假设第
i
i
i个位置开始最多可以扩展到
f
i
f_i
fi位置,以
d
p
[
i
]
[
j
]
dp[i][j]
dp[i][j]表示从
i
i
i开始扩展
2
j
2^j
2j次能够达到的最远距离,那么有转移
d
p
[
i
]
[
0
]
=
f
i
+
1
,
d
p
[
i
]
[
j
]
=
d
p
[
d
p
[
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
之后对于每组查询,从
l
l
l开始倍增跳即可,时间复杂度
O
(
n
l
o
g
2
n
)
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;
}