2024年牛客寒假基础训练营5EFJK

E.soyorin的数组操作(easy)

题面:

soyorin有一个长度为 nnn 的数组,她每次操作可以选择一个不超过 n 的偶数 k ,使得 ai := ai+i (1​i​k)。

soyorin想知道她是否可以通过任意次操作将数组变成非降序。

思路:

如果说n为偶数,那么一定可以完成,每次进行加法计算,它们之前的差值都会变小。

如果说n为奇数,那么最后一个数不能进行加法计算,这时候就要开始进行操作了。因为数据比较大,如果从后往前进行判断,直接把当前的数变成比前一个小的最大值。直接统计当前的数应该加多少遍,如果直接改变数组里面的值那么就会爆掉。当然如果数组开int128那当我没有说,但是如果你开了int128就要用read()函数进行读取数数据。

#include <bits/stdc++.h>
#include <iostream>
using namespace std;
const int N=1e6+10;
#define rep(i,a,n) for(int i=a;i<=n;i++)
#define per(i,n,a) for(int i=n;i>=a;i--)
#define endl '\n'
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int,int> PII;
#define int long long
​
int a[N];
void solve()
{
    int n;    cin >> n;
    for(int i=0;i<=n+1;i++)    a[i]=0;
    for(int i=1;i<=n;i++)    cin >> a[i];
​
    if(n%2==0)
    {
        cout << "YES" << endl;
        return ;
    }
    
    int flag=true;
    int cnt=0;
    
    for(int i=n-1;i>=1;i--)
    {
        if(i%2==0)
        {
            __int128 x=(a[i+1]+cnt*(i+1));
            __int128 y=(a[i]+cnt*i);
    
            if(y>x)
            {
                flag=false;
                break;
            }
            int k=(x-y)/i;
            cnt+=k;
        }
        else if(i%2==1)
        {
            __int128 x=a[i]+i*cnt;
            __int128 y=a[i+1]+(i+1)*cnt;
            if(x>y)
            {
                flag=false;
                break;
            }
        }
    }
    
    if(flag)    cout << "YES" << endl;
    else    cout << "NO" << endl;
}
signed main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    int T=1;
    cin >> T;
    while(T--)
        solve();
}

F.soyorin的数组操作(hard)

题面:

soyorin有一个长度为 nnn 的数组,她每次操作可以选择一个不超过 nnn 的偶数 kkk ,使得 ai:=ai+i (1​i​k)。

soyorin想知道她将数组变成非降序的最小操作次数,若无解则输出 -1。

思路:

你只需要从左往右遍历一遍,求一下当前的数比下一个数大多少,这样我只要在这遍加max那么就变成相等的了。那么现在我只要判断一个是否可以成立,如果成立那么只直接输出相差的最大值,否则就直接输出-1。(判断的代码同上)

#include <bits/stdc++.h>
#include <iostream>
using namespace std;
const int N=1e6+10;
#define rep(i,a,n) for(int i=a;i<=n;i++)
#define per(i,n,a) for(int i=n;i>=a;i--)
#define endl '\n'
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int,int> PII;
#define int long long
​
int a[N];
void solve()
{
    int n;    cin >> n;
    for(int i=0;i<=n+1;i++)    a[i]=0;
    for(int i=1;i<=n;i++)    cin >> a[i];
    
    int ans=0;
    for(int i=1;i<n;i++)    ans=max(ans,a[i]-a[i+1]);
    
    if(n%2==0)
    {
        cout << ans << endl;
        return ;
    }
    
    int flag=true;
    int cnt=0;
    for(int i=n-1;i>=1;i--)
    {
        if(i%2==0)
        {
            __int128 x=(a[i+1]+cnt*(i+1));
            __int128 y=(a[i]+cnt*i);
    
            if(y>x)
            {
                flag=false;
                break;
            }
            int k=(x-y)/i;
            cnt+=k;
        }
        else if(i%2==1)
        {
            __int128 x=a[i]+i*cnt;
            __int128 y=a[i+1]+(i+1)*cnt;
            if(x>y)
            {
                flag=false;
                break;
            }
        }
    }
    
    if(flag)    cout << ans << endl;
    else    cout << -1 << endl;
}
signed main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    int T=1;
    cin >> T;
    while(T--)
        solve();
}

J.rikki的数组陡峭值

题面:

rikki定义一个数组的陡峭值为:数组相邻元素差的绝对值之和。例如 [1,1,4,5,1,4] 的陡峭值为 |1-1|+|1-4|+|4-5|+|5-1|+|1-4| = 0+3+1+4+3 = 11。

rikki现在有一个数组 aaa ,其中每个元素 ai​ 都可以在 [li,ri]中选择一个整数,求最小的陡峭值。

思路:

就是说在每个区间顺序取一个数,然后取一下最小值,用的是合并区间的思路

#include <bits/stdc++.h>
#include <iostream>
using namespace std;
const int N=1e6+10;
#define rep(i,a,n) for(int i=a;i<=n;i++)
#define per(i,n,a) for(int i=n;i>=a;i--)
#define endl '\n'
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int,int> PII;
#define int long long
​
void solve()
{
    int n;        cin >> n;
    int l[N],r[N];
    for(int i=1;i<=n;i++)        cin >> l[i] >> r[i];
    
    int L=l[1];    int R=r[1];//初始化两个边界
    int ans=0;//记录不同合并的区间的值
    
    for(int i=2;i<=n;i++)
    {
        L=max(L,l[i]);//不断地更新左边界
        R=min(R,r[i]);//不断地更新右边界
        if(L>R)
        {
            ans+=L-R;
            R=min(r[i],R);//取最小的边界
            L=max(l[i],R);//和L相比取最大的边界
            R=L;
        }
    }
    
    cout << ans << endl;
}
signed main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    int T=1;
    //cin >> T;
    while(T--)
        solve();
}

K.soyorin的通知

题面:

soyorin有一个消息,她要把这个消息通知给 n个人,初始时没有人被通知过。 soyorin可以花费 p 的代价将消息通知给 1 个人,也可以花费 ai 的代价让第 i个人将消息通知给至多 bi个人

(前提是第 i个人已经被通知过,并且这个操作的次数不限)。

soyorin想知道最少花费多少代价可以将消息通知给 n个人。

思路:

这就相当于一个完全背包,但是你要先初始化一下,因为p的话,他刚开始通知一个人,这个是必须要用的,可能用通知1~n个人用p的代价

因为求的是最小的个代价可以通知​n个人,然后你的边界就要和0进行取一下最大值,这样最后f[n]才能保证​n个人

#include <bits/stdc++.h>
#include <iostream>
using namespace std;
const int N=1e6+10;
#define rep(i,a,n) for(int i=a;i<=n;i++)
#define per(i,n,a) for(int i=n;i>=a;i--)
#define endl '\n'
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int,int> PII;
#define int long long
​
int f[N];
void solve()
{
    int n,p;    cin >> n >> p;
    for(int i=1;i<n;i++)    f[i]=i*p;
    
    for(int i=1;i<=n;i++)
    {
        int a,b;    cin >> a >> b;
        for(int j=0;j<n;j++)
            f[j]=min(f[j],f[max(0LL,j-b)]+a);
    }
    cout << f[n-1]+p << endl;
}
signed main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    int T=1;
    //cin >> T;
    while(T--)
        solve();
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值