基础优化c++

1.前缀和:

求一段区间的和
预处理的复杂度是O(n)
访问的复杂度是O(1)
s [ i ] = s [ i − 1 ] + a [ i ] s[i]=s[i-1]+a[i] s[i]=s[i1]+a[i]
s [ 1 ] = s [ 0 ] + a [ 1 ] s[1]=s[0]+a[1] s[1]=s[0]+a[1]

最大子段和

题目描述

给出一个长度为 n n n 的序列 a a a,选出其中连续且非空的一段使得这段和最大。

输入格式

第一行是一个整数,表示序列的长度 n n n

第二行有 n n n 个整数,第 i i i 个整数表示序列的第 i i i 个数字 a i a_i ai

输出格式

输出一行一个整数表示答案。

样例 #1

样例输入 #1

7
2 -4 3 -1 2 -4 3

样例输出 #1

4

提示

样例 1 解释

选取 [ 3 , 5 ] [3, 5] [3,5] 子段 { 3 , − 1 , 2 } \{3, -1, 2\} {3,1,2},其和为 4 4 4

数据规模与约定
  • 对于 40 % 40\% 40% 的数据,保证 n ≤ 2 × 1 0 3 n \leq 2 \times 10^3 n2×103
  • 对于 100 % 100\% 100% 的数据,保证 1 ≤ n ≤ 2 × 1 0 5 1 \leq n \leq 2 \times 10^5 1n2×105 − 1 0 4 ≤ a i ≤ 1 0 4 -10^4 \leq a_i \leq 10^4 104ai104
    在这里插入图片描述
    在这里插入图片描述
    我们要求绿色的区块,绿色的区块的值是:蓝色的区块-黄色的区块
#include <bits/stdc++.h>
using namespace std;
int a[1000005],sum[1000005],summin[1000005],n;
int main()
{
	cin>>n;
	for(int i=1;i<=n;i++)
	{
		cin>>a[i];
		sum[i]=sum[i-1]+a[i];	//前缀和
	}
	for(int i=1;i<=n;i++)
	{
		summin[i]=min(summin[i-1],sum[i]);
	}
	int res=-2e9;	//a数组的最大值是负数
	for(int i=1;i<=n;i++)
	{
		res=max(res,sum[i]-summin[i-1]);	//找
	}
	cout<<res<<endl;
	return 0;
}

2.差分:

能多次修改一段区间上的值最后求整个区间的值
原理:
a 1 = b 1 {a_1}={b_1} a1=b1
a 2 = b 1 + b 2 {a_2}={b_1}+{b_2} a2=b1+b2
a 3 = b 1 + b 2 + b 3 {a_3}={b_1}+{b_2}+{b_3} a3=b1+b2+b3
a 4 = b 1 + b 2 + b 3 + b 4 {a_4}={b_1}+{b_2}+{b_3}+{b_4} a4=b1+b2+b3+b4
a n = b 1 + b 2 + . . . . . . + b n {a_n}={b_1}+{b_2}+......+{b_n} an=b1+b2+......+bn
想让第2项到第100项都加1:
b 2 {b_2} b2+= 1 1 1, b 1 01 {b_101} b101-= 1 1 1
想让第i项到第r项加d:
b l {b_l} bl+=d, b r + 1 {b_{r+1}} br+1-= d d d

构造差分:

初始: a 1 、 a 2 、 a 3 、 a 3 、 a 4 、 . . . . . . 、 a 1000 、 . . . . . . 、 a n {a_1}、{a_2}、{a_3}、{a_3}、{a_4}、......、{a_{1000}}、......、{a_n} a1a2a3a3a4......a1000......an
a : a: a:-5 6 0 0 0 0
b : b: b:-5 11 -6 0 0 0

语文成绩

题目背景

语文考试结束了,成绩还是一如既往地有问题。

题目描述

语文老师总是写错成绩,所以当她修改成绩的时候,总是累得不行。她总是要一遍遍地给某些同学增加分数,又要注意最低分是多少。你能帮帮她吗?

输入格式

第一行有两个整数 n n n p p p,代表学生数与增加分数的次数。

第二行有 n n n 个数, a 1 ∼ a n a_1 \sim a_n a1an,代表各个学生的初始成绩。

接下来 p p p 行,每行有三个数, x x x y y y z z z,代表给第 x x x 个到第 y y y 个学生每人增加 z z z 分。

输出格式

输出仅一行,代表更改分数后,全班的最低分。

样例 #1

样例输入 #1

3 2
1 1 1
1 2 1
2 3 1

样例输出 #1

2

提示

对于 40 % 40\% 40% 的数据,有 n ≤ 1 0 3 n \le 10^3 n103

对于 60 % 60\% 60% 的数据,有 n ≤ 1 0 4 n \le 10^4 n104

对于 80 % 80\% 80% 的数据,有 n ≤ 1 0 5 n \le 10^5 n105

对于 100 % 100\% 100% 的数据,有 n ≤ 5 × 1 0 6 n \le 5\times 10^6 n5×106 p ≤ n p \le n pn,学生初始成绩 $ \le 100 , , z \le 100$。

#include<bits/stdc++.h>
using namespace std;
int d[5000001];
int a[5000001];
int main()
{
	int n,p,x,y,z,i,min=1e9;	//min的初始值一定要设的很大
	cin>>n>>p;
	for(i=1;i<=n;i++)
	{
		cin>>a[i];
	}
	
    for(i=1;i<=n;i++)
	{
		d[i]=a[i]-a[i-1];	//前缀和
	}
	for(i=0;i<p;i++)
	{
		cin>>x>>y>>z;	//差分
		d[x]+=z;
		d[y+1]-=z;
	}
	
	for(i=1;i<=n;i++)
	{
		a[i]=a[i-1]+d[i];	//转换
		if(min>a[i])	//找最小值
		{
			min=a[i];
		}
	}
	cout<<min;
	return 0;
} 

逛画展

题目描述

博览馆正在展出由世上最佳的 m m m 位画家所画的图画。

游客在购买门票时必须说明两个数字, a a a b b b,代表他要看展览中的第 a a a 幅至第 b b b 幅画(包含 a , b a,b a,b)之间的所有图画,而门票的价钱就是一张图画一元。

Sept 希望入场后可以看到所有名师的图画。当然,他想最小化购买门票的价格。

请求出他购买门票时应选择的 a , b a,b a,b,数据保证一定有解。

若存在多组解,输出 a a a 最小的那组

输入格式

第一行两个整数 n , m n,m n,m,分别表示博览馆内的图画总数及这些图画是由多少位名师的画所绘画的。

第二行包含 n n n 个整数 a i a_i ai,代表画第 i i i 幅画的名师的编号。

输出格式

一行两个整数 a , b a,b a,b

样例 #1

样例输入 #1

12 5
2 5 3 1 3 2 4 1 1 5 4 3

样例输出 #1

2 7

提示

数据规模与约定
  • 对于 30 % 30\% 30% 的数据,有 n ≤ 200 n\le200 n200 m ≤ 20 m\le20 m20
  • 对于 60 % 60\% 60% 的数据,有 n ≤ 1 0 5 n\le10^5 n105 m ≤ 1 0 3 m\le10^3 m103
  • 对于 100 % 100\% 100% 的数据,有 1 ≤ n ≤ 1 0 6 1\leq n\le10^6 1n106 1 ≤ a i ≤ m ≤ 2 × 1 0 3 1 \leq a_i \leq m\le2\times10^3 1aim2×103
    这题要用双指针!
    第一步:找到第一个r的位置,从 a [ 1 ] a[1] a[1] ~ a [ r ] a[r] a[r] m m m
#include <bits/stdc++.h>
int m[2001],num;
int n[1000000];
int main()
{
    int R=-1,L=0,N,M,t,i=0,ansL,ansR;
    scanf("%d%d",&N,&M);
    for(i=0;i<N;i++)
    {
    	scanf("%d",n+i);
    }
    i=0;
    while(num!=M)
    {
        if(m[n[i]]==0)num++;	//装桶
        m[n[i]]++;
        R++;	//找r的值
        i++;
    }
    while(m[n[L]]>1)
    {
        m[n[L++]]--;
    }
    ansL=L;ansR=R;
    while(i<N)
    {
        m[n[i]]++;
        R++;
        i++;
        while(m[n[L]]>1)
            m[n[L++]]--;
        if(ansR-ansL>R-L)
        {
            ansR=R;
            ansL=L;
        }
    }
    printf("%d %d",ansL+1,ansR+1);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值