1. 程式人生 > >Newcoder 82 B.區間的連續段(貪心+ST)

Newcoder 82 B.區間的連續段(貪心+ST)

Description

給你一個長為nn的序列aa和一個常數kk

mm次詢問,每次查詢一個區間[l,r][l,r]內所有數最少分成多少個連續段,使得每段的和都 k\le k

如果這一次查詢無解,輸出Chtholly"“Chtholly"

Input

第一行三個數n,m,kn,m,k

第二行nn個數表示這個序列aa

之後mm行,每行給出兩個數l,rl,r表示一次詢問

(1n,m106,1ai,k109)(1\le n,m\le 10^6,1\le a_i,k\le 10^9)

106,1ai,k109)

Output

輸出mm行,每行一個整數,表示答案

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

為分成儘可能少的段顯然要把每一段儘可能擴充套件,假設第ii個位置開始最多可以擴充套件到fif_i位置,以dp[i][j]dp[i][j]表示從ii開始擴充套件2j2^j次能夠達到的最遠距離,那麼有轉移 dp[i][0]=fi+1,dp[i][j]=dp[dp[i][j1]][j1],j>0 dp[i][0]=f_i+1,dp[i][j]=dp[dp[i][j-1]][j-1],j>0

之後對於每組查詢,從ll開始倍增跳即可,時間複雜度O(nlog2n)O(nlog_2n)

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;
}