8-3陈老师课堂总结--程皓楠

· · 个人记录

P1102 A-B 数对

函数代替手写

1. lower_bound(a+1,a+1+n,x)-a

这个函数能够求出最小的满足a[i]>=x的地址,但我们要求下标,所以要用地址-地址=下标

2. upper_bound(a+1,a+1+n,x)-a

这个函数能够求出最小的满足a[i]>x的地址,但我们要求下标所以要用地址-地址=下标

题目思路

我们可以把A-B=C转化成A-C=B,然后一定要变成有序序列,然后用二分第一个大于b的下标-第一个大于等于b的下标从而求出b的个数

AC代码

#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N=1e6+5;
int a[N];
signed main()
{
    ios::sync_with_stdio(false);
    cin.tie(0),cout.tie(0);
    int n,c;
    cin>>n>>c;
    for(int i=1;i<=n;i++)cin>>a[i];
    sort(a+1,a+1+n);
    int sum=0;
    for(int i=1;i<=n;i++)sum+=(upper_bound(a+1,a+1+n,a[i]-c)-lower_bound(a+1,a+1+n,a[i]-c));
    cout<<sum;
    return 0;
}

A-CF1006C Three Parts of the Array

简化题意

给定一个长度为n的数列d,你需要将它分成从左到右3个连续的子数列,每个数字属于且仅属于其中一个子数列。要求第一个数列的元素值之和sum1等于第三个数列元素之和sum3。第二个数列可以没有元素。求sum1的最大值。

题目思路

通过题意,我们可以发现我们重点研究的是sum1和sum3,sum2并无大用。看到和,我们立即可以立即想到前缀和,然而这道题的数据大小又让我们想到O(nlogn),所以这道题的算法我们可以猜到是前缀和+二分

我们可以先算出数列d的前缀和,然后判断若二分出第一个大于等于s[n]-s[i+1]的值的下标>=i并且s[n]-s[i+1]要等于[1,二分的值的下标]之和就废止

AC代码

#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N=2e6+5;
int s[N];
signed main()
{
    ios::sync_with_stdio(false);
    cin.tie(0),cout.tie(0);
    int n,ans=0;
    cin>>n;
    for(int i=1;i<=n;i++)
    {
        int x;
        cin>>x;
        s[i]=s[i-1]+x;
    }
    for(int i=1;i<=n;i++)
    {
        int l=i+1,r=n;
        int pos=lower_bound(s+1,s+1+n,s[r]-s[l-1])-s;
        if(pos>=i&&s[pos]==s[r]-s[l-1])ans=s[i];
    }
    cout<<ans;
    return 0;
}

B-Glider

题目思路

这道题目我们可以枚举起跳的点,在这之前,我们用两个前缀和求出变的距离和不变的距离,然后二分高度,由于下降的距离是已知的,所以我们要取不下降距离的最大值,每次都用二分高度的下标-起跳点,取最大和,最后输出用不下降最大距离+下降距离

AC代码

#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N=2e5+5;
int s[N],e[N],sum[N],pre[N];
signed main()
{
    ios::sync_with_stdio(false);
    cin.tie(0),cout.tie(0);
    int n,k;
    cin>>n>>k;
    for(int i=1;i<=n;i++)
    {
        cin>>s[i]>>e[i];
        if(i>1)sum[i]=sum[i-1]+(s[i]-e[i-1]);
        pre[i]=pre[i-1]+(e[i]-s[i]);
    } 
    int ans=0;
    for(int i=1;i<=n;i++)
    {
        int pos=lower_bound(sum+i+1,sum+1+n,sum[i]+k)-sum-1;
        ans=max(ans,(pre[pos]-pre[i-1]));
    }
    cout<<ans+k;
    return 0;
}

C - Romantic Glasses

题目思路

用两个前缀和求出奇数下标和,还有偶数下标和,然后用桶统计sum1[r]-sum2[r]的差出现了几次,找出重复的差,若有就输出YES,等循环结束后,若还没输出YES,则输出NO

AC代码

#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N=2e6+5;
int a[N],sum1[N],sum2[N];
signed main()
{
    ios::sync_with_stdio(false);
    cin.tie(0),cout.tie(0);
    int T;
    cin>>T;
    while(T--)
    {
        map<int,int> mp;
        int n;
        cin>>n;
        for(int i=1;i<=n;i++)
        {
            cin>>a[i];
            if(i%2==1)
            {
                sum1[i]=sum1[i-1]+a[i];
                sum2[i]=sum2[i-1];
            }
            else
            {
                sum2[i]=sum2[i-1]+a[i];
                sum1[i]=sum1[i-1];
            }
        }
        bool f=1;
        for(int r=0;r<=n;r++)
        {
            mp[sum1[r]-sum2[r]]++;
            if(mp[sum1[r]-sum2[r]]>1)
            {
                cout<<"YES\n";
                f=0;
                break;
            }
        }
        if(f)cout<<"NO\n";
        mp.clear();
    }
    return 0;
}