IMUTOJ 1073 植树(莫队算法)

本文通过一道关于植树问题的竞赛题介绍了莫队算法的应用。该算法适用于离线处理区间查询问题,通过预先分块和排序查询来优化计算效率。文章详细讲解了算法实现过程,并附带完整的代码示例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目:点击打开链接(需校园网)

1073: 植树

时间限制: 1 Sec   内存限制: 128 MB
提交: 103   解决: 0
[ 提交][ 状态][ 讨论版]

题目描述

为了地球家园的清新美丽,软件班的孩子们去植树了……他们一共栽了N棵树,这些树的高度是不同的,它们的编号分别为1,2,3…N,树的高度都是正整数。现在班主任给同学们提出了问题:在编号L到编号R之间,高度为H的树的数量恰好也为H的一共有多少种?1<=L<=R<=N。

输入

多组输入数据,每组第一行两个正整数N和M,1<=N,M<=100000,以空格分隔,分别代表树的数量以及班主任问题的数量。第二行输入N个正整数分别代表这N棵树的高度h,1<=h<=1000000000,以空格分隔。从第三行开始,每行输入一个班主任的问题,问题参数是两个位置的编号L和R, 1<=L<=R<=N。输入以文件尾结束。

输出

对于每组输入,按顺序输出班主任所有M个问题的答案,每个答案占一行。每一组输出完成后,再输出一个空行。

样例输入

7 2
3 1 2 2 3 3 7
1 7
3 4
10 2
1 2 2 3 3 3 4 4 4 4
1 6
5 10

样例输出

3
1

3
1

莫队算法讲解:https://www.cnblogs.com/CsOH/p/5904430.html

思路:

先写个结构体,加个构造函数,分块。

然后排序,每次询问由上次的结果移动一下左右节点,得到这次的。

特殊处理一下第一个数是1且区间内只有他一个的情况。

#include<bits/stdc++.h>
using namespace std;
const int maxm=100005;
int len;
int read(){
    int x=0,f=1;char ch=getchar();
    while(ch>'9'||ch<'0'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
struct Query{
    int L, R, ID, block;
    Query(){}
    Query(int l, int r, int ID):L(l), R(r), ID(ID){
        block = l / len;
    }
    bool operator < (const Query rhs) const{
        if(block == rhs. block) return R<rhs.R;
        return block < rhs.block;
    }
}queries[maxm];

int high[maxm],cnt,ans[maxm];

inline void insert(int n){
    high[n]++;
    if(high[n]==n)
        cnt++;
}

inline void erase(int n){
    high[n]--;
    if(high[n]+1==n&&high[n]!=n)
        cnt--;
    if(high[n]==n)
        cnt++;
}

int A[maxm];
queue<int> anss[maxm];

int main(){
    int n, m;
    while(scanf("%d%d",&n,&m)!=EOF){
        cnt=0;
        memset(high,0,sizeof(high));
        len = (int)sqrt(n);
        for(int i = 1; i <= n; i++)
            A[i]=read();
        for(int i = 1; i <= m; i++){
            int l, r;
            l=read(),r=read();
            queries[i] = Query(l, r, i);
        }
        sort(queries + 1, queries + m + 1);
        int L = 1, R = 1;
        high[A[1]] = 1;
        for(int i = 1; i <= m; i++){
            Query &qi = queries[i];
            while(R < qi.R) insert(A[++R]);
            while(L > qi.L) insert(A[--L]);
            while(R > qi.R) erase(A[R--]);
            while(L < qi.L) erase(A[L++]);
            if(high[1]==1&&A[1]==1) cnt++;
            ans[queries[i].ID]=cnt;
        }
        for(int i = 1; i <= m; i++)
            printf("%d\n",ans[i]);
        printf("\n");
    }
}

### 华为OD算法——植树距离计算与实现 #### 问题分析 在解决植树距离问题时,核心目标是找到一种策略,在给定的坑位数量和位置基础上,使得所种植树木之间的最小间距最大化。此问题可以通过二分查找法结合贪心算法来高效求解。 具体来说,通过二分查找确定可能的最大最小间距 `mid`,并利用贪心算法验证在此间距条件下是否能够满足所需种植的树苗数量 \(m\)。如果可以,则尝试更大的间距;否则缩小范围继续寻找最优解[^1]。 以下是基于 Java 的完整解决方案及其优化建议: --- #### 算法实现 ```java import java.util.Arrays; import java.util.Scanner; public class TreePlantingDistance { public static void main(String[] args) { Scanner scanner = new Scanner(System.in); // 输入坑位数量 int n = scanner.nextInt(); int[] positions = new int[n]; // 输入坑位的具体位置 for (int i = 0; i < n; i++) { positions[i] = scanner.nextInt(); } // 需要种植的树苗数量 int m = scanner.nextInt(); // 调用函数获取结果 System.out.println(findMaxMinDistance(positions, m)); } /** * 主逻辑:使用二分查找配合贪心算法解决问题 */ private static int findMaxMinDistance(int[] positions, int m) { Arrays.sort(positions); // 对坑位按升序排列 // 定义初始边界条件 int low = 1, high = positions[positions.length - 1] - positions[0], result = 0; while (low <= high) { int mid = (low + high) / 2; // 中间值作为候选间隔 if (canPlaceTrees(positions, m, mid)) { // 判断当前间隔是否可行 result = mid; // 更新结果为更大值 low = mid + 1; // 尝试更优解 } else { high = mid - 1; // 缩小搜索空间 } } return result; // 返回最终最大化的最小间距 } /** * 辅助函数:判断以指定最小间距 plantingInterval 是否能放置足够的树苗 */ private static boolean canPlaceTrees(int[] positions, int requiredCount, int plantingInterval) { int placedCount = 1; // 初始化已种树计数器(默认首棵) int lastPosition = positions[0]; // 上一棵树的位置 for (int currentPosition : positions) { if ((currentPosition - lastPosition) >= plantingInterval) { // 如果当前位置符合条件 placedCount++; lastPosition = currentPosition; // 更新上一颗树的位置 if (placedCount >= requiredCount) break; // 提前终止循环 } } return placedCount >= requiredCount; // 检查是否达到需求 } } ``` --- #### 关键点解析 1. **排序操作** 坑位数组需按照从小到大顺序排列以便后续处理更加直观有效。 2. **二分查找框架构建** 使用二分查找技术快速定位合适的最小种植间距区间上下限,并逐步逼近理想答案[^3]。 3. **贪心策略应用** 在每次迭代中评估当前假设的距离是否允许完成既定任务量级的目标设定。若成立则记录该试探值并向更高方向探索可能性;反之调整至较低端重新考量[^2]。 4. **时间复杂度分析** 排序部分的时间开销为 O(N log N),其中 N 表示坑位总个数; 另外每轮检查过程涉及线性扫描整个序列,整体效率较高且稳定可靠。 --- #### 进一步优化思考 尽管上述方案已经具备良好的性能表现,但仍可以从以下几个方面着手改进: - **减少不必要的比较次数**: 当确认某次试验失败后立即退出内部遍历而非等到结束再返回判定状态。 - **动态调整步长参数**: 根据实际情况灵活改变增量大小从而加速收敛速度。 - **引入缓存机制存储中间结果**, 特别是在多次调用相同子结构的情况下可显著降低重复运算负担。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值