Educational Codeforces Round 152 (Rated for Div. 2) A-D

教育场被教育了

A. Morning Sandwich

题意:

制作一个汉堡,汉堡必须是两面包夹芝士或者汉堡肉的形式(可以有很多层),现有b片面包,c片芝士和h片汉堡肉,问最多能制作多少层的汉堡

题解:

#define _CRT_SECURE_NO_WARNINGS
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<vector>
using namespace std;
typedef long long LL;
typedef unsigned long long ULL;
typedef pair<int, int> PII;
const LL N = 1e5 + 10, MOD = 998244353, INF = 0x3f3f3f3f;
void solve()
{
    int n, a, b;
    scanf("%d%d%d", &n, &a, &b);
    a += b;
    printf("%d\n", min(n - 1, a) * 2 + 1);
}
int main()
{
    int T = 1;
    scanf("%d", &T);
    while (T--)
    {
        solve();
    }
    return 0;
}

B. Monsters

题意:

有n只怪物,每只怪的生命为ai,你只有一种攻击方式,锁定一名敌方当前生命值最高的单位并对他造成k点伤害(若有多个生命值相同的单位优先锁定编号小的那个),问你击杀怪物的顺序

题解:

优先攻击生命最高的单位,在某次攻击后敌方生命会全部小于等于k(全部变为(ai-1)%k+1),在这之后你将开始击杀并会优先锁定生命值高的,因此击杀顺序是优先击杀(ai-1)%k+1较大的,可以用map<int,vector>来存下然后反着输出。

#define _CRT_SECURE_NO_WARNINGS
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<vector>
#include<map>
using namespace std;
typedef long long LL;
typedef unsigned long long ULL;
typedef pair<int, int> PII;
const LL N = 1e5 + 10, MOD = 998244353, INF = 0x3f3f3f3f;
map<int, vector<int> >mp;
void solve()
{
    mp.clear();
    int n, k;
    scanf("%d%d", &n, &k);
    for (int i = 1, x; i <= n; ++i)
    {
        scanf("%d", &x);
        mp[(x - 1) % k + 1].push_back(i);
    }
    auto it = mp.rbegin();
    while (it != mp.rend())
    {
        for (auto i : it->second)
            printf("%d ", i);
        ++it;
    }
    printf("\n");
}

int main()
{
    int T = 1;
    scanf("%d", &T);
    while (T--)
    {
        solve();
    }
    return 0;
}

C. Binary String Copying

题意:

给定一长度为n的01串,以该01串为原串生成m个新串,生成的字符串为对原串下标l到r的区间进行排序之后的新串,问这些新串中有多少个是不同的(去重之后还剩几个)

题解:

可以记录排序之后新串相对于原串的变化并用set去重。先处理原串中'1'的数量的前缀和以及对于每个位置来说他后面最近的0的位置,每次更改后区间l到r内的1都会挤到区间的后面,那我们就可以记录修改之后的情况:修改之后在位置i之前有连续k个1,存入{i,k}。特别的,当排序的区间内的1本来就都在末尾的情况排序之后与原串没有变化,这时候需要特殊处理一下(例如存入{0,0})

#define _CRT_SECURE_NO_WARNINGS
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<vector>
#include<set>
using namespace std;
typedef long long LL;
typedef unsigned long long ULL;
typedef pair<int, int> PII;
const LL N = 2e5 + 10, MOD = 998244353, INF = 0x3f3f3f3f;
char ch[N];
int ne[N], cnt[N];
set<PII>st;
void solve()
{
    st.clear();
    int n, m;
    scanf("%d%d%s", &n, &m, ch + 1);
    ne[n + 1] = n + 1;
    for (int i = 1, j = n; i <= n; ++i, --j)
    {
        cnt[i] = cnt[i - 1] + ch[i] - '0';
        if (ch[j] == '1')
            ne[j] = ne[j + 1];
        else
            ne[j] = j;
    }
    for (int i = 1; i <= m; ++i)
    {
        int l, r;
        scanf("%d%d", &l, &r);
        int c = cnt[r] - cnt[l - 1];
        if (cnt[r] - cnt[r - c] == c)
        {
            st.insert({ 0,0 });
            continue;
        }
        c += ne[r + 1] - r - 1;
        st.insert({ ne[r + 1] ,c });
    }
    printf("%d\n", st.size());
}
int main()
{
    int T = 1;
    scanf("%d", &T);
    while (T--)
    {
        solve();
    }
    return 0;
}

D. Array Painting

题意:

你有一个长度为n的数组a(0<=ai<=2),刚开始他们都是蓝色,你的目标是把他们都染成红色,你有一下两种操作:1、选择一个蓝色的点并把它染成红色,2、选择一个ai>0的红色的点并选择一个与它相邻的蓝色点,将其染成红色并使ai减一。求最少操作次数。

题解:

贪心。对于一个存在2的连续的只有1或2的区间就相当于一个2,对于一个连续的只存在1的区间就相当于一个1(应该挺好想的)。只要先把题目给的数组优化一下从左往右贪心即可(优化后整个数组就不存在连续的1或2,所有的1或2都被0分割,相对起来好处理)。

#define _CRT_SECURE_NO_WARNINGS
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<vector>
#include<set>
using namespace std;
typedef long long LL;
typedef unsigned long long ULL;
typedef pair<int, int> PII;
const LL N = 2e5 + 10, MOD = 998244353, INF = 0x3f3f3f3f;
int a[N];
vector<int>v;
void solve()
{
    v.clear();
    int n, ans = 0;
    scanf("%d", &n);
    for (int i = 1; i <= n; ++i)
        scanf("%d", &a[i]);
    for (int i = 1; i <= n; ++i)
    {
        if (a[i])
        {
            int f = 0;
            while (i <= n && a[i])
            {
                if (a[i] == 2)f = 1;
                ++i;
            }
            if (f)v.push_back(2);
            else v.push_back(1);
            --i;
        }
        else
            v.push_back(0);
    }
    for (int i = 0; i < v.size(); ++i)
    {
        //先染色i+1然后让后面的数进行2操作染色i
        if (i + 1 < v.size() && v[i + 1])--v[i + 1];
        else ++ans;//直接染色i
        if (v[i])++i;//染色i之后对i进行2操作染色i+1
    }
    printf("%d\n", ans);
}

int main()
{
    int T = 1;
    //scanf("%d", &T);
    while (T--)
    {
        solve();
    }
    return 0;
}

E. Max to the Right of Min

不会写的题

题意:

给出一个1到n的排列,定义minposl,r为区间l到r范围内最小值的下表,maxposl,r同理,求给出排列中存在多少个区间满足minposl,r<maxposl,r。

不会解:

不会写题解,我的评价是看这篇:Educational Codeforces Round 152 (Rated for Div. 2) A - F - 知乎 (zhihu.com) 这个思路感觉挺好的

代码抄了jiangly的,加了一点没啥用的注释

//基本CV了jiangly的E题代码
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<vector>
using namespace std;
typedef long long LL;
typedef unsigned long long ULL;
typedef pair<int, int> PII;
const LL N = 1e6 + 10, MOD = 998244353, INF = 0x3f3f3f3f;
int a[N], lmin[N], rmin[N], lmax[N], rmax[N];
int erfen(vector<int>&v,int l,int r, int t)
{
    while (l < r)
    {
        int mid = l + r >> 1;
        if (rmin[v[mid]] > t)
            l = mid + 1;
        else
            r = mid;
    }
    return l - 1;
}
void solve()
{
    int n;
    scanf("%d", &n);
    for (int i = 1; i <= n; ++i)
        scanf("%d", &a[i]);
    //单调栈处理每个点左/右比它小/大的第一个点的下标(不存在则为0/n+1)
    vector<int>smin, smax;
    for (int i = 1; i <= n; ++i)
    {
        while (smin.size() && a[smin.back()] > a[i])
        {
            rmin[smin.back()] = i;
            smin.pop_back();
        }
        if (smin.size())
            lmin[i] = smin.back();
        smin.push_back(i);

        while (smax.size() && a[smax.back()] < a[i])
        {
            rmax[smax.back()] = i;
            smax.pop_back();
        }
        if (smax.size())
            lmax[i] = smax.back();
        smax.push_back(i);
    }
    while (smin.size())
    {
        rmin[smin.back()] = n + 1;
        smin.pop_back();
    }
    while (smax.size())
    {
        rmax[smax.back()] = n + 1;
        smax.pop_back();
    }
    //以上都是单调栈处理

    LL ans = 0;
    vector<int>v{ 0 };//单调栈维护左边比ai小的下标集合
    vector<LL>sum{ 0LL };//前缀和
    for (int i = 1; i <= n; ++i)
    {
        //考虑计数以i为最大值下表的方案
        while (v.size() > 1 && a[v.back()] > a[i])
        {
            v.pop_back();
            sum.pop_back();
        }
        //首先左选点最多到lmax[i],右选点最多到rmax[i](集合中不能有元素比ai大)
        //minpos的选择最多到单调栈中的第l个↓
        int l = upper_bound(v.begin(), v.end(), lmax[i]) - v.begin();
        if (l < v.size())
        {
            //大致分成三部分做
            //1、minpos为v[l]时的方案数
            ans += 1LL * (v[l] - max(v[l - 1], lmax[i])) * (min(rmax[i], rmin[v[l]]) - i);
            
            //二分找到最后一个rmin[v[m]]>rmax[i]
            int m = erfen(v, l + 1, v.size(), rmax[i]);
            //2、左选点在v[l+1]到v[m]时右选点最大都为rmax[i]
            ans += 1LL * (v[m] - v[l]) * (rmax[i] - i);

            //3、左选点在v[m+1]到lmin[i]时右选点最大为各自的rmin[v[j]],这里使用前缀和处理
            //Σ(v[j]-lmin[v[j]])*(rmin[v[j]]-i),(这里m+1<=j<=v.size()-1)
            //=Σ(v[j]-lmin[v[j]])*rmin[v[j]]-Σ(v[j]-lmin[v[j]])*i
            //=(sum.back()-sum[m]) - (lmin[i]-v[m])*i; 
            ans -= 1LL * (lmin[i] - v[m]) * i;
            ans += sum.back() - sum[m];
        }
        //计算前缀和,因为对于每个i会分别减去多算的部分所以这里是直接*rmin[i]而不是(rmin[i]-几)
        sum.push_back(sum.back() + 1LL * (i - lmin[i]) * rmin[i]);
        v.push_back(i);
    }
    printf("%lld\n", ans);
}

int main()
{
    int T = 1;
    //scanf("%d", &T);
    while (T--)
    {
        solve();
    }
    return 0;
}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值