【237题】算法基础精选题单:Day1刷题笔记:前缀和和差分

NC16649 校门外的树

P5568 [NOIP2005 普及组] 校门外的树

[NOIP2005 普及组] 校门外的树//NC24636 值周(同样的代码)

题目描述

某校大门外长度为 l l l 的马路上有一排树,每两棵相邻的树之间的间隔都是 1 1 1 米。我们可以把马路看成一个数轴,马路的一端在数轴 0 0 0 的位置,另一端在 l l l 的位置;数轴上的每个整数点,即 0 , 1 , 2 , … , l 0,1,2,\dots,l 0,1,2,,l,都种有一棵树。

由于马路上有一些区域要用来建地铁。这些区域用它们在数轴上的起始点和终止点表示。已知任一区域的起始点和终止点的坐标都是整数,区域之间可能有重合的部分。现在要把这些区域中的树(包括区域端点处的两棵树)移走。你的任务是计算将这些树都移走后,马路上还有多少棵树。

输入格式

第一行有两个整数,分别表示马路的长度 l l l 和区域的数目 m m m

接下来 m m m 行,每行两个整数 u , v u, v u,v,表示一个区域的起始点和终止点的坐标。

输出格式

输出一行一个整数,表示将这些树都移走后,马路上剩余的树木数量。

样例 #1

样例输入 #1

500 3
150 300
100 200
470 471

样例输出 #1

298

提示

【数据范围】

  • 对于 20 % 20\% 20% 的数据,保证区域之间没有重合的部分。
  • 对于 100 % 100\% 100% 的数据,保证 1 ≤ l ≤ 1 0 4 1 \leq l \leq 10^4 1l104 1 ≤ m ≤ 100 1 \leq m \leq 100 1m100 0 ≤ u ≤ v ≤ l 0 \leq u \leq v \leq l 0uvl

【题目来源】

NOIP 2005 普及组第二题

错因:没有考虑到两个集合之间的重合关系,光想着排序完,判前后区间重叠就万事大吉了,其次边界处理的也不是很好,题目要求0为开始,即一开始拥有n+1棵树,跟一般前缀和大同小异

#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N = 2e6 + 10;
typedef pair<int, int> pii;
int n, m;
pii s[N];
signed main()
{
    cin >> n >> m;
    n += 1;
    for (int i = 0; i < m; i++)
    {
        int a, b;
        cin >> a >> b;
        s[i] = {a, b};
    }
    sort(s, s + m);
    int last = -1;
    int cnt = 0;
    for (int i = 0; i < m; i++)
    {
        int l = s[i].first;
        int r = s[i].second;
        if(r<=last)continue;
        else if (l <= last)
        {
            cnt = cnt + r - last;
        }
        else
            cnt = cnt + r - l + 1;
        // cout<<cnt<<endl;
        last = r;
    }
    cout << n - cnt << endl;
}

P1627 [CQOI2009] 中位数//[CQOI2009]中位数图

[CQOI2009] 中位数

题目描述

给出 1 , 2 , . . . , n 1,2,...,n 1,2,...,n 的一个排列,统计该排列有多少个长度为奇数的连续子序列的中位数是 b b b。中位数是指把所有元素从小到大排列后,位于中间的数。

输入格式

第一行为两个正整数 n n n b b b,第二行为 1 , 2 , . . . , n 1,2,...,n 1,2,...,n 的排列。

输出格式

输出一个整数,即中位数为 b b b 的连续子序列个数。

样例 #1

样例输入 #1

7 4
5 7 2 4 3 1 6

样例输出 #1

4

提示

数据规模与约定

对于 30 % 30\% 30% 的数据中,满足 n ≤ 100 n \le 100 n100

对于 60 % 60\% 60% 的数据中,满足 n ≤ 1000 n \le 1000 n1000

对于 100 % 100\% 100% 的数据中,满足 n ≤ 100000 , 1 ≤ b ≤ n n \le 100000,1 \le b \le n n100000,1bn

一开始感觉和前缀和没有任何关系,但后来才发现,根据题目要求,可以将数据分为两类:

  1. 比b大的,转换为1
  2. 比b小的,转换为-1,记录b的出现位置pos(注意是排列,即只会出现一次)
  3. 题目转换为求包含b的,区间和为0的奇数区间,那就能运用到前缀和了
  4. 求出前面pos-1的后缀和以及pos+1后面的前缀和
  5. 将答案分为三类:1.只包含左边。2.只包含右边 3.左右都有(这个可以先用哈希表记录前面的sum各个数值出现的次数,然后既然要求区间和为0且横跨左右区间,针对于右区间的各个sum值,左边的-sum(负sum)将会贡献答案)
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N = 2e6 + 10;
typedef pair<int, int> pii;
int n, m;
int s[N];
map<int ,int>M;
// sum[N*2];
//int sumd[N*2];
signed main()
{
   cin>>n>>m;
   int pos;
   for(int i=0;i<n;i++)
   {
      int temp;cin>>temp;
      if(temp>m) s[i]=1;
      else if(temp<m) s[i]=-1;
      else pos=i;
   }
   int sum=0;
   int ans=0;
   for(int i=pos-1;i>=0;i--) 
   {
        sum+=s[i];
       if(!sum) ans++;
       M[sum]++;
   }
    sum=0;
   for(int i=pos+1;i<n;i++)
   {
      sum+=s[i];
      if(!sum) ans++;
      ans+=M[-sum];
   }
   cout<<ans+1<<endl;
  
}

[HNOI2003]激光炸弹

[HNOI2003] 激光炸弹

题目描述

一种新型的激光炸弹,可以摧毁一个边长为 m m m 的正方形内的所有目标。现在地图上有 n n n 个目标,用整数 x i x_i xi , y i y_i yi 表示目标在地图上的位置,每个目标都有一个价值 v i v_i vi。激光炸弹的投放是通过卫星定位的,但其有一个缺点,就是其爆破范围,即那个边长为 m m m 的边必须与 x x x 轴, y y y 轴平行。若目标位于爆破正方形的边上,该目标不会被摧毁。

现在你的任务是计算一颗炸弹最多能炸掉地图上总价值为多少的目标。

可能存在多个目标在同一位置上的情况。

输入格式

输入的第一行为整数 n n n 和整数 m m m

接下来的 n n n 行,每行有 3 3 3 个整数 x , y , v x, y, v x,y,v,表示一个目标的坐标与价值。

输出格式

输出仅有一个正整数,表示一颗炸弹最多能炸掉地图上总价值为多少的目标(结果不会超过 32767 32767 32767 )。

样例 #1

样例输入 #1

2 1
0 0 1
1 1 1

样例输出 #1

1

提示

数据规模与约定

  • 对于 100 % 100\% 100% 的数据,保证 1 ≤ n ≤ 1 0 4 1 \le n \le 10^4 1n104 0 ≤ x i , y i ≤ 5 × 1 0 3 0 \le x_i ,y_i \le 5\times 10^3 0xi,yi5×103 1 ≤ m ≤ 5 × 1 0 3 1 \le m \le 5\times 10^3 1m5×103 1 ≤ v i < 100 1 \le v_i < 100 1vi<100

注意:1. 其下标开始为0,0;而前缀和的起始下标从一开始,所以要预先处理输入
思路:题目可转换成:给出每个点的价值,要求求出二维上某个正方形区域价值总和的最大值,显然是二维前缀和+枚举

#include <bits/stdc++.h>
using namespace std;
#define int long long

const int N = 5100;
int n, m;
int s[N+10][N+10];
signed main()
{
    cin >> n >> m;
    int ans = 0;
    int edx = 0, edy = 0;
    for (int i = 1; i <= n; i++)
    {
        int x, y, w;
        cin >> x >> y >> w;
        s[++x][++y] += w;
    }
    for (int i = 1; i <= N; i++)
    {
        for (int j = 1; j <= N; j++)
        {
            s[i][j] = s[i][j] + s[i - 1][j] + s[i][j - 1] - s[i - 1][j - 1];
        }
    }
    for (int i = m; i <= N; i++)
    {
        for (int j = m; j <= N; j++)
        {
            int now = s[i][j] - s[i - m][j] - s[i][j - m] + s[i - m][j - m];
            ans = max(now, ans);
        }
    }
    cout << ans << endl;
}

}

NC207053 二分

链接:https://ac.nowcoder.com/acm/problem/207053
来源:牛客网

题目描述
我们刚刚学了二分查找——所谓二分查找就是在一堆有序数里找某个符合要求的数。在学完二分查找之后如果让你玩猜数游戏(裁判选定一个目标数字,你说一个数裁判告诉你是高了还是低了直到你猜到那个数)的话,显然你会用二分的方式去猜。
但是不是每一个玩猜数游戏的人都知道二分是最好,甚至一个健忘的玩家都有可能在得到裁判回答的下一个瞬间就忘了他之前问了什么以及裁判的回答),而现在更可怕的是,这个告诉你猜的数是高还是低的裁判他也很健忘,他总是薛定谔的记得这个目标数字,也就是说他的回答有可能出错。我们已经不关心这个不靠谱的游戏本身了,我们更关心裁判这个薛定谔的记得到底有几个是记得…
现在给出这个健忘的玩家的所有猜测和裁判的所有回答,问裁判最多能有多少次是记得目标数字的,即裁判的回复是符合情况的。
输入描述:
第一行包含一个正整数n,表示裁判的回答数(也是玩家的猜数次数)。
接下来n行,首先是猜的数,然后是一个空格,然后是一个符号。符号如果是“+”说明猜的数比答案大,“-”说明比答案小,“.”说明猜到了答案。
输出描述:
包含一个正整数,为裁判最多有多少个回答是正确的。

第一次这道题还真是一头雾水,看了题解之后才明白其中的巧妙之处

  1. 题目求裁判最多有多少个回答是正确的,而输入样例给出了’.‘(猜中)’-‘(大)’+'(小)三种情况,
  2. 于是可以考虑将三种情况转换为区间操作,则答案即求对于某个猜中的数,其被覆盖的区间数量的最大值。
  3. 而用差分来维护一个个区间,用前缀和去更新ans的最大值(+1->区间内每个数都满足这个条件,反过来讲,这里面每个数都被一个区间覆盖,-1->出了区间,外面的数不再满足这个条件,也不存在于这个区间)
  4. 由于题目n较小而猜数的范围较大,最好用map去保序离散化。
#include <bits/stdc++.h>
using namespace std;
#define int long long

const int N = 3010;
int n, m;
map<int, int> M;
signed main()
{
    cin >> n;
    for (int i = 0; i < n; i++)
    {
        int a;
        char b;
        cin >> a >> b;
        if (b == '.')
        {
            M[a]++;
            M[a + 1]--;
        }
        else if (b == '-')
        {
            M[a+1]++;
        }
        else {M[a]--;M[1]++;}
    }
    int sum=0;
    int ans=0;
    for(auto temp:M)
    {
        int u=temp.second;
        sum+=u;
        ans=max(ans,sum);
    }
    cout<<ans<<endl;
}

链接:https://ac.nowcoder.com/acm/problem/50937
来源:牛客网

NC50937 货仓选址

题目描述
在一条数轴上有N家商店,它们的坐标分别为 A[1]~A[N]。现在需要在数轴上建立一家货仓,每天清晨,从货仓到每家商店都要运送一车商品。为了提高效率,求把货仓建在何处,可以使得货仓到每家商店的距离之和最小。
输入描述:
第一行一个整数N,第二行N个整数A[1]~A[N]。
输出描述:
一个整数,表示距离之和的最小值。
示例1
输入
复制
4
6 2 9 1
输出
复制
12
备注:
对于100%的数据:
𝑁

100000
N≤100000,
𝐴
[
𝑖
]

1000000
A[i]≤1000000

#include <bits/stdc++.h>
using namespace std;
#define int long long

const int N = 2e5+10;
int n, m;
int s[N];
signed main()
{
    cin >> n;
    for(int i=0;i<n;i++)
    cin>>s[i];
    sort(s,s+n);
    int l=0;int r=n-1;
    int ans=0;
    while(l<r)
    {
        if(l!=r)
        ans=ans+s[r--]-s[l++];
    }
    cout<<ans<<endl;
}

  • 5
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值