牛客小白月赛86题解

牛客小白月赛86

https://ac.nowcoder.com/acm/contest/73450#question

A

注意一下数据类型就行

#include <iostream>
using namespace std;
int main()
{
    int T;
    cin>>T;
    while(T--)
    {
    int  a ,b ,c,d;
    cin>>a>>b>>c>>d;
    a*d > c*b?cout<<"S"<<endl:cout<<"Y"<<endl;
    }
}

B

分类讨论

笔记: find()没找到返回-1

#include <bits/stdc++.h>
using namespace std;
int main()
{
    int  n;
    cin>>n;
    while(n--)
    {
        string a, b;
        cin>>a>>b;
        if(a == b)puts("10");
        else 
        {
            int ok = 1;
            for(auto c:a)
            {
                if(b.find(c) == -1)ok = 0;
            }
            if(ok)puts("10");
            else puts("0");
        }
    }
    return 0;
}

C

如果暴力处理询问会超时
本题考点是前缀和
很重要的性质是a[i]和a[i-1]不同的话,就会分段
把从1 到 n 的段数前缀和,最后询问[l,r] 相当于询问 sum[r] - sum[l] +1

举个例子
6 1
2 2 3 1 3 3
1 3
sum[r] == sum[3] ==2 {2,2}{3}
sum[l] == sum[1] ==1 {2}

#include <bits/stdc++.h>
using namespace std;
const int N = 2e5 + 10;
int a[N],sum[N],n,m;
int main()
{
    scanf("%d %d",&n,&m);
    for(int i = 1 ;i <= n; i++)scanf("%d",&a[i]);
    for(int i = 1 ;i <= n; i++)sum[i] = sum[i-1] + (a[i]!=a[i-1]);
    while(m --)
    {
        int l ,r;
        scanf("%d %d",&l,&r);
        cout<<sum[r] - sum[l] + 1 <<endl;
    }
    return 0;
}

D

暂时跳过,作者还没学dfs,过年事情多,学完来补
也可以评论提醒一下~~

E

题意是在一个数组中找一个子串,在满足子串的w之和(饱腹值)至少是W的情况下,使得这个子串的d之和(可口值)最大
我们从前缀和入手
求和,可能会爆int,别问,问就是int一个测试点不对

不妨让区间是 [ L , R ]
枚举 起点 L
对于每一个起点 , 找到 最小的R ,让区间[L , R] 的w之和大于等于W
方法就是对w求前缀和 ,lower_bound出 r0
二分的性质是大于等于sumw[R] >=W+sumw[L-1]
对于 [ r0 , n ],都满足w之和大于等于W
接下来是寻找d之和最大
d的表达式是sumd[R] - sumd[L-1]
即寻找 [ r0 , n ]区间里sumdR最大值,重新维护一个maxd数组,maxd[i] 表示 i-n区间sumdR的最大值
把maxd[r0] - sumd[L - 1]作为L为起点的结果,枚举所有起点,维护ans

#include <bits/stdc++.h>
using namespace std;
const int N  = 1e6 + 10;
long long sumw[N],sumd[N],maxd[N];
int n , W;

int main()
{
    scanf("%d %d",&n, &W);
    for(int i = 1;i <= n; i++)
    {
        scanf("%lld", &sumw[i]);
        sumw[i] = sumw[i - 1] + sumw[i];
    }
    for(int i = 1;i <= n; i++)
    {
        scanf("%lld",&sumd[i]);
        sumd[i] = sumd[i - 1] + sumd[i];
    }
    maxd[n+1] = -1e15;
    for(int i = n;i >= 1;i--)
    {
        maxd[i] = max(maxd[i+1],sumd[i]);
    }
    long long ans = -1e15;
    for(int L = 1 ;L <= n; L ++)
    {
        int R = lower_bound(sumw + L,sumw + n + 1,sumw[L-1]+W) - sumw;
        ans = max(ans,maxd[R] - sumd[L-1]);
    }
    printf("%lld",ans);
}

最开始我没有用maxd,直接在sumd上求后缀的最大值了,导致最后sumd[L-1]数据不是原来的sumd[L-1]

F

看到等差数列,就想到用差分来做,然后暴力搜1结果TLE了,正解是set维护每一段今天补个题解,最难的就是work函数

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const LL N = 2e5+10;
LL a[N] , ans;
set<pair<int,int>> S;
int n, m;


void del(LL l,LL r)
{
    S.erase({l,r});
    ans -= (r - l + 1)*(r - l + 1);
}


void add(LL l,LL r)
{
    S.insert({l,r});
    ans += (r - l + 1)*(r - l + 1);
}


void inse(LL a[],LL l,LL r,LL c)
{
    a[l] += c;
    a[r + 1] -= c;
}


void work(int  pos,LL c)
{
    if(pos==1 || pos==n+1)return ;
    a[pos]+=c;
    if(a[pos]==1)
    {
        // (    1     ) -> (   )x(    )
        // (         1)(    ) ->  (      )(x)(     )
        auto [l, r] = *S.lower_bound({pos,-1});
        auto [l1,_] = *prev(S.lower_bound({pos,-1}));
        del(l, r);
        del(l1,pos - 1);
        add(l1,r);
    }
    else if(a[pos]-c==1)
    {
        //(   )(x)(    ) -> (         )
        //(    )(x     ) -> (         )
        //(           )(x) ->(        )
        auto [l,r] = *prev(S.lower_bound({pos,-1}));
        del(l ,r);
        add(l ,pos - 1);
        add(pos, r);
    }
}
int main()
{
    cin.tie(0);
    cout.tie(0);
    cin>> n >> m;
    int x;
    for(int i = 1; i <= n; i++)
    {
        cin>>x;
        inse(a , i , i , x);
    }
    for(int i = 1; i<= n; i++)
    {
        int j = i + 1;
        while(a[j]==1 && j <= n)j++;
        j--;
        add(i,j);
        i = j;
    }
    LL c;
    int l ,r;

    while(m --)
    {
        cin>>l>>r>>c;
        if(c)
        {
            work(l,c);
            work(r+1,-c);    
        }
        printf("%lld\n",ans);
    }
    return 0;
}


评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值