2021-11-16每日刷题打卡

2021-11-16每日刷题打卡

AcWing——算法基础

797. 差分 - AcWing题库

输入一个长度为 n 的整数序列。

接下来输入 m 个操作,每个操作包含三个整数 l,r,c,表示将序列中 [l,r ] 之间的每个数加上 c。

请你输出进行完所有操作后的序列。

输入格式

第一行包含两个整数 n 和 m。

第二行包含 n 个整数,表示整数序列。

接下来 m 行,每行包含三个整数 l,r,c 表示一个操作。

输出格式

共一行,包含 n 个整数,表示最终序列。

数据范围

1≤n,m≤100000
1≤l≤r≤n
−1000≤c≤1000
−1000≤整数序列中元素的值≤1000

输入样例:
6 3
1 2 2 1 2 1
1 3 1
3 5 1
1 6 1
输出样例:
3 4 5 3 4 2

昨天我们学到的前缀和用到的是前缀和数组,今天这里我们用到的是逆前缀和数组,也叫差分数组。先介绍一下差分数组是什么:我们知道,前缀和数组的各位数,就是原数组上的前面所有数的和,比如原数组是1 2 3 4 5,那么前缀和数组的第三位就是前三个数的和,即6,这就是前缀和数组。而逆前缀和就说,原数组是这个差分数组的前缀和数组(关系逆转了),比如原数组是1 2 3 4 5,那么差分数组就该是 1 1 1 1 1,这样原数组上的第四位数,就是差分数组的1~4之和,即4。差分数组各位上的运算如下:(a为原数组,b为差分)b[i]=a[i]-a[i-1],不理解的可以想一下:b[1]=a[1]-a[0]、b[2]=a[2]-a[1]、b[3]=a[3]=a[2],这样b1+b2+b2就等于a3了。

然后为什么把区间[l,r]加上c要用差分数组呢,如果我们按正常的情况,就是遍历a中[l,r]位置上的数,然后把c加上去,时间复杂度是n,如果多次重复这种操作容易超时。而差分数组可以化简这一操作,比如正常的数组为1 2 3 4 5,现在你要把[2,4]上的数都加上3,原数组就变成1 5 6 7 5,而差分数组只要把l位置上的数加上3即可,即 1 4 1 1 1 ,因为原数组上的数是差分数组的前缀和,那么只要l上加上c,那么l和l之后的所有数都会加上c了,但有一点注意的是,我们加的数是有区间范围的,比如说上面的例子,我们只想24位置上的数+3,但用拆分数组算后发现第5位数也加上了3变成了8,这不是我们想要的,所以我们要在区间之外的数都-3,即差分数组r+1位置上的数-3即可,这样l位置前的数不会收到+3的影响,lr位置上的数会+3,r之后的数会+3后-3,即不会变化。这样就可以很方便的把原数组l~r区间的数都加上c了,比起对原数组遍历,差分数组只用在l和r+1上两个位置做动作,时间复杂度为O(1)。

#include<iostream>
using namespace std;
const int N=100010;
int n,m;
int a[N],b[N];

int main()
{
    cin>>n>>m;
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&a[i]);
        b[i]=a[i]-a[i-1];
    }
    while(m--)
    {
        int l,r,c;
        cin>>l>>r>>c;
        b[l]+=c;
        b[r+1]-=c;
    }
    for(int i=1;i<=n;i++)
    {
        a[i]=a[i-1]+b[i];
        cout<<a[i]<<" ";
    }
    return 0;
}
798. 差分矩阵 - AcWing题库

输入一个 n 行 m 列的整数矩阵,再输入 q 个操作,每个操作包含五个整数 x1,y1,x2,y2,c,其中 (x1,y1)和 (x2,y2) 表示一个子矩阵的左上角坐标和右下角坐标。

每个操作都要将选中的子矩阵中的每个元素的值加上 cc。

请你将进行完所有操作后的矩阵输出。

输入格式

第一行包含整数 n,m,q。

接下来 n 行,每行包含 m 个整数,表示整数矩阵。

接下来 q 行,每行包含 5 个整数 x1,y1,x2,y2,c 表示一个操作。

输出格式

共 n 行,每行 m 个整数,表示所有操作进行完毕后的最终矩阵。

数据范围

1≤n,m≤1000
1≤q≤100000
1≤x1≤x2≤n
1≤y1≤y2≤m
−1000≤c≤1000
−1000≤矩阵内元素的值≤1000

输入样例:
3 4 3
1 2 2 1
3 2 2 1
1 1 1 1
1 1 2 2 1
1 3 2 3 2
3 1 3 4 1
输出样例:
2 3 4 1
4 3 4 1
2 2 2 2

用的也是差分数组,解释起来有点长,感兴趣的可以通过公式+画图来理解

#include<iostream>
using namespace std;
const int N=1010;
int n,m,q;
int a[N][N],b[N][N];

void insert(int x1,int y1,int x2,int y2,int c)
{
    b[x1][y1]+=c;
    b[x1][y2+1]-=c;
    b[x2+1][y1]-=c;
    b[x2+1][y2+1]+=c;
}

int main()
{
    cin>>n>>m>>q;
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++)
        {
            scanf("%d",&a[i][j]);
            insert(i,j,i,j,a[i][j]);
        }
    while(q--)
    {
        int x1,y1,x2,y2,c;
        cin>>x1>>y1>>x2>>y2>>c;
        insert(x1,y1,x2,y2,c);
    }
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=m;j++)
        {
            b[i][j]+=b[i-1][j]+b[i][j-1]-b[i-1][j-1];
            cout<<b[i][j]<<" ";
        }
        cout<<endl;
    }
    return 0;
}
799. 最长连续不重复子序列 - AcWing题库

给定一个长度为 n 的整数序列,请找出最长的不包含重复的数的连续区间,输出它的长度。

输入格式

第一行包含整数 n。

第二行包含 n 个整数(均在 0∼10^5范围内),表示整数序列。

输出格式

共一行,包含一个整数,表示最长的不包含重复的数的连续区间的长度。

数据范围

1≤n≤10^5

输入样例:
5
1 2 2 3 5
输出样例:
3

准备两个数组,a[N],s[N],N开到100010。再用一个数res来存贮最长序列的长度。先把数都存入a数组里,然后用双指针i和j遍历a,初始i和j都为0,i每次遍历都++,当i>=a.size()时结束循环,i每走一格,s[a[i]]++,这里是为了计算i走过的位置是否有重复数,以a[i]为下标,s[a[i]]就为a[i]这个数出现的次数,然后判断,如果s[a[i]]>1,说明i走过的地方有重复数,此时j开始动,当s[a[i]]不大于1时j停下,每次s[a[j]]–,这里是为了找到和当前a[i]相同数字的位置,如果–后s[a[i]]还是大于1,说明还没找到和当前a[i]相同数的位置,j继续++,如果s[a[i]]不大于1了,就说明已经找到相同数的位置,j停止++。每次i++后,对比res和i-j+1的大小(i-j+1代表当前连续子序列的长度),取较大的那个给res赋值。当i走到数组尽头时结束遍历,输出res。

#include<iostream>
using namespace std;

const int N=100010;
int n;
int a[N],s[N];

int main()
{
    int res=0;
    cin>>n;
    for(int i=0;i<n;i++)scanf("%d",&a[i]);
    for(int i=0,j=0;i<n;i++)
    {
        s[a[i]]++;
        while(s[a[i]]>1)
        {
            s[a[j]]--;
            j++;
        }
        res=max(res,i-j+1);
    }
    cout<<res<<endl;
    return 0;
}
800. 数组元素的目标和 - AcWing题库

给定两个升序排序的有序数组 A 和 B,以及一个目标值 x。

数组下标从 0 开始。

请你求出满足 A[i]+B[j]=x 的数对 (i,j)。

数据保证有唯一解。

输入格式

第一行包含三个整数 n,m,x,分别表示 A 的长度,B 的长度以及目标值 x。

第二行包含 n 个整数,表示数组 A。

第三行包含 m 个整数,表示数组 B。

输出格式

共一行,包含两个整数 i 和 j。

数据范围

数组长度不超过 10^5。
同一数组内元素各不相同。
1≤数组元素≤10^9

输入样例:
4 5 6
1 2 4 7
3 4 6 8 9
输出样例:
1 1

双指针就是我亲人啊,我们以前写过一个数组从头到尾的,这次变成两个而已,没区别。一个指针从a数组的开头走,一个指针从b数组的尾部走,加一起大于若x就把b数组的指针往后挪,若小于x就把a数组指针往前挪,相等就停止遍历然后输出。(注意,双指针遍历的这种方式必须要求数组是有序的,不然会错)

#include<iostream>
using namespace std;
const int N=100010;
int n,m,x;
int a[N],b[N],c[N];

int main()
{
    cin>>n>>m>>x;
    for(int i=0;i<n;i++)scanf("%d",&a[i]);
    for(int i=0;i<m;i++)scanf("%d",&b[i]);
    int i=0,j=m-1;
    while(1)
    {
        if(a[i]+b[j]>x)j--;
        else if(a[i]+b[j]<x)i++;
        else break;
    }
    cout<<i<<" "<<j<<endl;
    return 0;
}
2816. 判断子序列 - AcWing题库

给定一个长度为 n 的整数序列 a1,a2,…,an 以及一个长度为 m 的整数序列 b1,b2,…,bm。

请你判断 a 序列是否为 b 序列的子序列。

子序列指序列的一部分项按原有次序排列而得的序列,例如序列 {a1,a3,a5} 是序列 {a1,a2,a3,a4,a5}的一个子序列。

输入格式

第一行包含两个整数 n,m。

第二行包含 n 个整数,表示 a1,a2,…,an

第三行包含 m 个整数,表示 b1,b2,…,bm

输出格式

如果 a 序列是 b 序列的子序列,输出一行 Yes

否则,输出 No

数据范围

1≤n≤m≤10^5
−109≤ai,bi≤109

输入样例:
3 5
1 3 5
1 2 3 4 5
输出样例:
Yes

双指针真的是我亲人!这两题是我目前唯二没看y总视频写出来的。

准备两个数组a、b,a数组用来存小的,b数组用来存大的,准备两个指针i、j,初始分别指向a、b的开头,然后开始遍历,当a[i]=b[j]上i才++,然后j一直往前走。当遍历结束后判断一下i是否走完了a数组,如果是就输出Yes,反之输出No。

#include<iostream>
using namespace std;

const int N=100010;
int n,m;
int a[N],b[N];

int main()
{
    cin>>n>>m;
    for(int i=0;i<n;i++)scanf("%d",&a[i]);
    for(int i=0;i<m;i++)scanf("%d",&b[i]);
    int i=0,j=0;
    while(j<m&&i<n)
    {
        if(a[i]==b[j])i++;
        j++;
    }
    if(i==n)cout<<"Yes"<<endl;
    else cout<<"No"<<endl;
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值