一类差分题的思考方式及经典例题

问题描述

这类题目一般都会给定一个整数序列A和一个大定义域D。定义一个与序列元素有关的函数f(X),定义域是D,值域是非负整数。

找到一个X,使得f(X)最大。

思考方式

虽然D的范围可能很大,但是f(X)>0X可能不多。因此我们只需要知道哪些Xf(X)>0,且这些f(x)的值是多少即可。

这类题一般都会先考虑序列中元素对哪些X有贡献,一个元素会使得一个区间[l,r]上的答案同增或同减一个常数(通常就是1)。比如:当X[l,r]aif(X)贡献了1

因此可以使用差分维护这个过程,最后求前缀和并更新答案即可。

因此我们对于具体题目的考虑就是,什么样的元素会影响哪个区间。

典型例题

题意:A1,A2,,An是一个由n个自然数(非负整数)组成的数组。我们称其中Ai,,Aj是一个非零段,当且仅当以下条件同时满足:1ijn;对于任意的整数k,若ikj,则Ak>0i=1Ai1=0j=nAj+1=0。现在我们可以对数组A进行如下操作:任选一个正整数p,然后将A中所有小于p的数都变为0。试选取一个合适的p,使得数组A中的非零段个数达到最大。若输入的A所含非零段数已达最大值,可取p=1,即不对A做任何修改。

解题思路:在这道题中,f(X)表示非零段个数。我们现在考虑什么样的元素会影响哪个区间。对于相邻两个数,如果Ai>Ai1,为了让Ai变为一个非零段的开始,我们需要把Ai1变为0。而[Ai1,Ai1]可以满足这个要求,也就是为这个区间上的f(X)都贡献了1。因此,Ai>Ai1这样的元素会使得[Ai1,Ai1]这个区间答案加1

注:岛(https://www.acwing.com/problem/content/2016/ )与本题几乎一样,只不过定义域更大,因此需要用map存差分。

代码:

#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>

using namespace std;

const int N = 500010, M = 10010;

int n;
int a[N], b[M];

void insert(int l, int r)
{
    b[l] ++, b[r + 1] --;
}

int main()
{
    scanf("%d", &n);
    for(int i = 1; i <= n; i ++) {
        int x;
        scanf("%d", &x);
        a[i] = x;
        if(a[i] > a[i - 1]) insert(a[i - 1], a[i] - 1);
        //如果选择[a[i - 1], a[i] - 1],那么第i个元素开始就称为了非零段
        //因此区间内所有数字贡献+1
    }
    int ans = 0;
    for(int i = 0; i < M; i ++) {
        b[i] = b[i - 1] + b[i];
        ans = max(ans, b[i]);
    }
    printf("%d\n", ans);
    return 0;
}

题意:有一群大人和小孩,每个人都有一个高度Ai。你需要划定一个高度标注X,并且你认为大于等于X的人都是大人,小于X的都是小孩。问:找到一个X,使得判断准确人数最多。

思路:在这道题中,f(X)表示判断准确人数。我们现在考虑什么样的元素会影响哪个区间。对于一个人i,当X处于哪个区间才能将其分对呢?如果这个人是大人,那么[0,Ai]是可以分对的,Ai对这个区间贡献1;如果这个人是小孩,那么[Ai+1,]可以分对的,Ai对这个区间贡献1

代码:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <map>

using namespace std;

const int N = 200010;

int n;
char s[N];
map<int, int> mp;

void insert(int l, int r)
{
    mp[l] ++, mp[r + 1] --;
}

int main()
{
    scanf("%d%s", &n, s + 1);
    for(int i = 1; i <= n; i ++) {
        int x;
        scanf("%d", &x);
        if(s[i] == '1') insert(0, x);
        else insert(x + 1, 1e9);
    }
    int ans = 0, t = 0;
    for(auto p : mp) {
        t += p.second;
        ans = max(ans, t);
    }
    printf("%d\n", ans);
    return 0;
}
posted @   pbc的成长之路  阅读(65)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 使用C#创建一个MCP客户端
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示