• 牛客练习赛14B 区间的连续段


    题目链接

    点我跳转

    题目大意

    给定一个长度为 \(N\) 的序列 \(A\) 和一个常数 \(K\)

    \(M\) 次询问

    每次询问查询一个区间 \([L , R]\) 内所有数最少分成多少个连续段

    使得每段的和都 \(<= K\) ,若无解则输出 "\(Chtholly\)"

    解题思路

    简单回忆一下倍增求 \(LCA\) 思想:

    • \(f[i][j]\) 表示以 \(i\) 为起点,往上跳 \(i + 2^j\) 步后得到的祖先
    • 因为往上跳 \(2^j\) 等价于先往上跳 \(2^{j - 1}\) 步后再往上跳 \(2^{j - 1}\)
    • 所以可得: \(f[i][j] = f[f[i][j - 1]][j - 1]\)

    回到这道题:

    暴力的做法即遍历区间 \([l,r]\) ,贪心的让每段的长度尽可能大

    考虑用倍增思想优化:

    定义 \(f[i][j]\) 表示:

    \(i\) 为起点,分成 \(2 ^ j\) 个连续段后,所能到达的最远位置的下一个位置(其中每个段的和都不超过 \(K\)

    那么不难得到: \(f[i][j] = f[f[i][j - 1]][j - 1]\)\(f[i][0]\) 可通过二分前缀和得到

    然后对于每个询问 \((L , R)\)

    先判断区间 \([L,R]\) 是否存在 \(A_i\) 使得 \(A_i > K\)

    这步我们维护一个权值数组的前缀和 \(O1\) 判断

    即当 \(A_i <= K\) 时,\(sum[i] = sum[i - 1]\)

    \(A_i > K\)时,\(sum[i] = sum[i - 1] + 1\)

    \(sum[R] - sum[L - 1] > 0\) 则表示该区间存在 \(A_i > K\),直接输出 \(Chtholly\)

    \(sum[R] - sum[L - 1] = 0\) 则从高位往低位枚举 \(j\)

    如果 \(f[L][j] > R\) 则表示从 \(L\) 开始划分出 \(2^j\) 个连续段是 \(OK\)
    但是 \(2^j\) 连续段可能太多了(题目要求划分的连续段个数最少
    所以就继续往下枚举

    如果 \(f[L][j] < R\),则表示从 \(L\) 开始划分出 \(2^j\) 个连续段是不够的
    那就先划分出 \(2^j\) 个连续段,然后再从 \(f[L][j]\) 的位置继续划分
    \(ans += 1 << j\)\(L = f[L][j]\)

    AC_Code

    #include<bits/stdc++.h>
    
    using namespace std;
    
    const int N = 1e6 + 10;
    
    int f[N][22];
    
    int n , m , k , a[N] , sum[N];
    
    long long pre[N];
    
    signed main()
    {
    	cin >> n >> m >> k;
    	
    	for(int i = 1 ; i <= n ; i ++)
    	{
    		cin >> a[i] , pre[i] = pre[i - 1] + a[i];
    		
    		sum[i] = sum[i - 1] + (a[i] > k); 
    	}
    	for(int j = 0 ; j <= 21 ; j ++) f[n + 1][j] = n + 1;
    	
    	for(int j = 0 ; j <= 21 ; j ++)
    	{
    		for(int i = 1 ; i <= n ; i ++)
    		{
    			f[i][0] = upper_bound(pre + i , pre + 1 + n , k - a[i] + pre[i]) - pre;
    			
    			if(!j) continue ;
    			
    			f[i][j] = f[f[i][j - 1]][j - 1]; 
    		}
    	}
    	while(m --)
    	{
    		int l , r , ans = 0;
    		
    		cin >> l >> r;
    		
    		if(sum[r] - sum[l - 1]) 
    		{
    			cout << "Chtholly\n";
    			
    			continue ;
    		}
    		for(int j = 21 ; j >= 0 ; j --)
    		{
    			if(f[l][j] - 1 < r) 
    			{
    				ans += 1 << j;
    				
    				l = f[l][j];
    			}
    		}
    		cout << ans + 1 << '\n';
    	}
    	return 0;
    }
    
  • 相关阅读:
    移动端测试作业小集合 (6)
    移动端测试——手机常见操作的API (5)
    移动端测试——APP元素信息、事件操作、模拟手势API(4)
    移动端测试——APP元素定位操作 (3)
    移动端测试——App基础操作(2)
    移动端测试基础 (1)
    Python进阶-一切皆对象及type-object-class间的关系
    故障-解决pip安装mysqlclient、gevent报找不到cc或gcc错误问题
    Linux环境上部署Flask
    解决多版本共存时,python/pip等命令失效
  • 原文地址:https://www.cnblogs.com/GsjzTle/p/14702955.html
Copyright © 2020-2023  润新知