2023河南萌新联赛第(二)场:河南工业大学

A.自动收小麦机

模拟,但不能完全模拟

非资深MC玩家第一次听说可以用水收小麦,如果加个漏斗的话是不是就自动存箱子里了

第一发模拟,TLE了,痛定思痛,开了个前缀和打算优化一下,结果忘开longlong又wa了,再次痛腚思痛,就能过了

模拟应该都会,这里给出优化思路:不再挨个遍历k个方块,而是从左侧第k-1个方块看,如果这个方块和当前起点方块高度一样(水不再继续往左流),直接结束用前缀和计算收小麦的数量然后输出即可,如果左侧第k-1个方块高度低于当前起点方块高度,则从这个第k-1个方块起,找离当前起点方块最近的,和第k-1个方块高度一样的方块(有点绕),把找到的这个方块更新为起点,循环判断直到收到最左边的方块或者水不再继续往左流为止

#include <iostream>

using namespace std;

const int N = 1e5 + 20;
long long n, m, k, x, q[N], h[N];

int main()
{
    int t;
    scanf("%lld%d%lld",&n,&t,&k);
    
    for(int i = 1; i <= n; i ++)
    {
        scanf("%lld",&q[i]);
        q[i] += q[i - 1];
    }
    
    for(int i = 1; i <= n; i ++)
    {
        scanf("%lld",&h[i]);
    }
    
    while(t --)
    {
        long long y;
        scanf("%lld",&x);
        
        y = x;
        while(1)
        {
            if(y - k + 1 <= 1)
            {
                y = 1;
                break;
            }
            else if(h[y - k + 1] == h[y])
            {
                y = y - k + 1;
                break;
            }
            else
            {
                for(int i = y - k + 1; i <= y; i ++)
                {
                    if(h[i] < h[i + 1])
                    {
                        y = i;
                        break;
                    }
                }
            }
        }
        printf("%lld\n", q[x] - q[y - 1]);
    }
    return 0;
}

D.固执的RT

一道看似正常的签到题(不正常的地方在于我忘了开longlong上来WA了一发)

#include <iostream>

using namespace std;

long long n, m, ans;

int main()
{
	int x;
	scanf("%lld%lld",&n,&m);
    
    while(n --)
    {
        cin >> x;
        ans += x;
    }
	
	if(ans >= m) puts("YES");
    else puts("NO");
	return 0;
}

E.释怀的RT

差分(这次倒是不用开longlong了,结果数组开小了又WA一发,QAQ)

用s数组存每一个格子能被几块心岩照亮对于下标为x的心岩,如果它能照亮的范围为k,则更新s数组:

左边k个:s[i - k] += 1, s[i] -= 1;

右边k个:s[i + 1] += 1, s[i + k + 1] -= 1;

最后将差分数组求前缀和,遍历s数组,计算能被大于0块心岩照亮的格子的个数即可

#include <iostream>

using namespace std;

const int N = 1e6 + 20;
int n, m, s[N];

int main()
{
	int t, l, r;
	scanf("%d",&n);
	for(int i = 1; i <= n; i ++)
    {
        scanf("%d",&t);
        l = max(0, i - t);
        r = min(n, i + t);
        
        if(t)
        {
            s[l] += 1;
            s[i] -= 1;
            s[i + 1] += 1;
            s[r + 1] -= 1;
        }
    }
	
    int ans = 0;
    for(int i = 1; i <= n; i ++)
    {
        s[i] = s[i - 1] + s[i];
        if(s[i]) ans ++;
    }
    
    printf("%d", ans);
    
	return 0;
}

G.爬山

二分

数组没开小,longlong也没忘,结果最后输出的时候输出了mid,又wa一发,难搞

二分初始范围为0~最高的山的高度(这样应该能少分几次)

通过check函数判断当前的mid是否可以让小明达到运动指标,需要注意的是,如果check(mid)<运动指标,需要将r缩小而不是将l增大

#include <iostream>

using namespace std;

const int N = 1e5 + 20;
int n, p, q[N];

long long check(int x)
{
    long long res = 0;
    
    for(int i = 1; i <= n; i ++)
    {
        if(q[i] > x)
        {
            res += 2 * (q[i] - x);
        }
    }
    
    return res;
}

int main()
{
    int l = 0, r = 0;
    scanf("%d%d",&n,&p);
    
    for(int i = 1; i <= n; i ++)
    {
        scanf("%d",&q[i]);
        
        r = max(r, q[i]);
    }
    
    int mid;
    while(l < r)
    {
        mid = (l + r + 1) / 2;
        if(check(mid) >= p) l = mid;
        else r = mid - 1;
    }
    
    if(check(l) >= p) printf("%d", l);
    else puts("-1");
    
    return 0;
}

I.奶牛的寿命

位运算

你说你一个奶牛闲的没事招惹上帝那老瘪犊子干嘛,是没看到普罗米修斯的下场吗?

按位与,看看二进制的n有多少个1,第一位留一个1,剩下的从后往前排即可

#include <iostream>

using namespace std;

int main()
{
    int n, cnt = 0;
    scanf("%d",&n);
    
    int x = n, y = 0;
    while(x)
    {
        y ++;
        if(x & 1) cnt ++;
        x >>= 1;
    }
    
    int res = 0;
    for(int i = y; i; i --)
    {
        res *= 2;
        if(i == cnt || i == y)
        {
            res += 1;
            cnt --;
        }
    }
    
    printf("%d", n - res);
    
    return 0;
}

L.最长连续相同字符串

线段树

补题要早补,不然就补晚了

连题解带模板不断摸索,总算是给搞出来了

我们知道,线段树的每一个非叶节点的状态都是由其子节点的状态更新出来的

所以,对于一个区间的最长连续相同字符, 可能由以下三种方式得到:

1.左子区间的最长连续相同字符

2.右子区间的最长连续相同字符

3.左子区间的最长连续相同后缀与右子区间最长连续相同前缀的拼接

由此,我们知道对于线段树的每一个节点, 我们要存储的信息有: 区间左右端点、最长连续相同前缀的具体信息、最长连续相同后缀的具体信息以及当前区间的最长连续相同字符的具体信息

当用子节点的状态去更新父节点的状态时, 需要用到的信息还有:最长连续相同前/后缀以及子区间最长连续相同字符的起始点、相同字符数量以及字符类型

为了方便使用,我们可以开一个结构体SEG用来存储这些信息

以下为代码实现:

#include <iostream>
#include <string>

using namespace std;

const int N = 1e5 + 20;
int n, m;
string s;

struct SEG{
    int l, r, cnt;
    char ch;
};

struct Node{
    int l, r;
    SEG lmax, rmax, tmax;
}tr[4 * N];

SEG max(SEG x, SEG y)
{
    if(x.cnt > y.cnt) return x;
    else if(x.cnt < y.cnt) return y;
    else
    {
        if(x.l < y.l) return x;
        return y;
    }
}

void push_up(Node &u, Node &l, Node &r)
{
    u.lmax = l.lmax;
    if(l.lmax.r == l.r && l.lmax.ch == r.lmax.ch)
    {
        u.lmax = {l.lmax.l, r.lmax.r, l.lmax.cnt + r.lmax.cnt, l.lmax.ch};
    }
    
    u.rmax = r.rmax;
    if(r.rmax.l == r.l && l.rmax.ch == r.rmax.ch)
    {
        u.rmax = {l.rmax.l, r.rmax.r, l.rmax.cnt + r.rmax.cnt, r.rmax.ch};
    }
    
    u.tmax = max(l.tmax, r.tmax);
    if(l.rmax.ch == r.lmax.ch)
    {
        SEG t = {l.rmax.l, r.lmax.r, l.rmax.cnt + r.lmax.cnt, l.rmax.ch};
        u.tmax = max(u.tmax, t);
    }
}

void push_up(int u)
{
    push_up(tr[u], tr[u << 1], tr[u << 1 | 1]);
}

Node query(int u, int l, int r)
{
    if(l <= tr[u].l && r >= tr[u].r) return tr[u];
    else
    {
        int mid = tr[u].l + tr[u].r >> 1;
        if(l > mid) return query(u << 1 | 1, l, r);
        else if(r <= mid) return query(u << 1, l, r);
        else
        {
            auto left = query(u << 1, l, r);
            auto right = query(u << 1 | 1, l, r);
            
            Node res;
            push_up(res, left, right);
            
            return res;
        }
    }
}

void modify(int u, int x, char ch)
{
    if(tr[u].l == tr[u].r) tr[u] = {x, x, {x, x, 1, ch}, {x, x, 1, ch}, {x, x, 1, ch}};
    else
    {
        int mid = tr[u].l + tr[u].r >> 1;
        if(x > mid) modify(u << 1 | 1, x, ch);
        else modify(u << 1, x, ch);
        
        push_up(u);
    }
}

void build(int u, int l, int r)
{
    if(l == r)
    {
        tr[u] = {l, r, {l, r, 1, s[l - 1]}, {l, r, 1, s[l - 1]}, {l, r, 1, s[l - 1]}};
    }
    else
    {
        tr[u] = {l, r};
        
        int mid = l + r >> 1;
        build(u << 1, l, mid), build(u << 1 | 1, mid + 1, r);
        
        push_up(u);
    }
}

int main()
{
    int k, x, y;
    scanf("%d%d",&n,&m);
    
    cin >> s;
    build(1, 1, n);
    

    char ch;
    while(m --)
    {
        scanf("%d",&k);
        if(k & 1) // 查询;
        {
            scanf("%d%d",&x,&y);
            
            auto t = query(1, x, y);
            printf("%d %d\n", t.tmax.l, t.tmax.r);
        }
        else // 修改;
        {
            scanf("%d %c",&x,&ch);
            modify(1, x, ch);
        }
    }
    return 0;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值