首页 > 其他分享 >Atcoder ABC 285 题解

Atcoder ABC 285 题解

时间:2023-01-17 22:55:12浏览次数:66  
标签:Atcoder ABC int 题解 add T1 休息日 ch 升序

E - Work or Rest

题意

​ 一周有 \(n\) 天,给出一个长度为 \(n\) 的数组 \(A\)。你可以决定一周中的休息日与工作日的分布,请问如何选择能够使总贡献最大。

​ 如何计算贡献:对于休息日,贡献为0;对于工作日,贡献为 \(A_{min(x, y)}\), \(x\) 是上一个休息日距今天多少天, \(y\) 是下一个休息日距今天多少天(若是在本周的末尾,后面不再有休息日的话,会找到下一周的第一个休息日)。

思路

​ 首先一眼可以知道是DP题,用 \(f[i]\) 表示前 \(i\) 天,且在第 \(i\) 天休息的最大价值。我首先想的是直接做,但是发现当前状态依赖于后面的状态,所以我们应该逆向思考一下。我们考虑通过确定休息日的位置来计算工作日的贡献。那状态状态方程就可以写出来了:

\[dp_i=max^{i}_{j=0} \{dp_j + calc(j, i)\} \]

​ \(clac(l,r)\) 表示 \(l\) 和 \(r\) 两个端点为休息日,中间皆为休息日的情况下的贡献。通过前缀和可以 \(O1\) 计算出来。又考虑到第 \(n\) 天可能会和下一周结合产生贡献,也就是环形问题。我们应该把休假的日子设成第 \(0\) 天,这样任何状态都可以直接从 \(dp[0]\) 转移过来。

代码

int n;
ll f[N]; //当前为i,上一个holiday为j天前

void solve()
{
    cin >> n;
    vector<ll> a(n + 1, 0), sum(n + 1, 0);
    for(int i = 1; i <= n; i ++)
        cin >> a[i], a[i] += a[i - 1];
    for(int i = 1; i <= n; i ++)
        sum[i] = a[i / 2] + a[(i - 1) / 2];

    for(int i = 1; i <= n; i ++)
        for(int j = 1; j <= i; j ++)
            f[i] = max(f[i], f[i - j] + sum[j]);

    ll res = 0;
    for(int i = 1; i <= n; i ++)
        res = max(res, f[i]);
    cout << res << '\n';
}


F - Substring of Sorted String

题意

​ 有一个长度为 \(10^5\) 的字符串。现在有 \(10^5\) 次更新和查询,更新:单点修改字符串中的一个字符;查询:截取字符串的区间 \([l, r]\) ,判断截取的串是否是升序排序后的原串的子串。

思路

​ 如何快速判断呢?我们转化一下,就是在询问截取的串是否升序掐头去尾后字符数量和原串中是相等的。我们需要维护每个字母数量的区间和且维护区间是否升序。还有掐头去尾可以用线段树维护一下区间最大最小值(其实也不用维护,如果升序的话 \(l\) 和 \(r\) 就是最小字母和最大字母,不升序的话也自然不合法),因为截取串中的最大字符和最小字符是可以不用跟原串相等的。

代码

const int N = 100005;
int n, m;
char a[N];

class BIT {
public:
    ll c[N] = {0};
    void add(int x, int val) {
        for (int i = x; i < N; i += i & -i) {
            c[i] += val;
        }
    }
    ll ask(int x) {
        ll ans = 0;
        for (int i = x; i; i -= i & -i) {
            ans += c[i];
        }
        return ans;
    }
} T1, cnt[26]; //维护是否递增,维护各个字母的数量区间和

struct Seg_tree {
    struct node {
        int l, r;
        int mx, mn;
    } seg[N << 2];
    void pushup(int k)
    {
        seg[k].mx = max(seg[k << 1].mx, seg[k << 1 | 1].mx);
        seg[k].mn = min(seg[k << 1].mn, seg[k << 1 | 1].mn);        
    }
    void build(int k, int l, int r)
    {
        seg[k].l = l, seg[k].r = r;
        if(l == r) {
            seg[k].mx = seg[k].mn = a[l] - 'a';
            return;
        }
        int mid = (l + r) >> 1; 
        build(k << 1, l, mid);
        build(k << 1 | 1, mid + 1, r);
        pushup(k);
    }
    void modify(int k, int pos, int v) 
    {
        int l = seg[k].l, r = seg[k].r;
        if(l == r) {
            seg[k].mx = seg[k].mn = v;
            return;
        }
        int mid = (l + r) >> 1;
        if(pos <= mid)
            modify(k << 1, pos, v);
        else
            modify(k << 1 | 1, pos, v);
        pushup(k);
    }
    int query_mx(int k, int ql, int qr)
    {
        int l = seg[k].l, r = seg[k].r;
        if(ql <= l && r <= qr)
            return seg[k].mx;
        int mid = (l + r) >> 1;
        int res = -inf;
        if(ql <= mid)
            res = max(res, query_mx(k << 1, ql, qr));
        if(mid < qr)
            res = max(res, query_mx(k << 1 | 1, ql, qr));
        return res;
    }
    int query_mn(int k, int ql, int qr)
    {
        int l = seg[k].l, r = seg[k].r;
        if(ql <= l && r <= qr)
            return seg[k].mn;
        int mid = (l + r) >> 1;
        int res = inf;
        if(ql <= mid)
            res = min(res, query_mn(k << 1, ql, qr));
        if(mid < qr)
            res = min(res, query_mn(k << 1 | 1, ql, qr));
        return res;
    }
} T2; //维护区间最大值最小值

int main()
{
    scanf("%d%s", &n, a + 1);
    T2.build(1, 1, n);
    for(int i = 1; i <= n; i ++)
        cnt[a[i] - 'a'].add(i, 1);
    for(int i = 1; i < n; i ++)
    {
        if(a[i] > a[i + 1]) //若非递增
            T1.add(i, 1);
    }

    scanf("%d", &m);
    while(m --)
    {
        int opt;
        scanf("%d", &opt);
        if(opt == 1)
        {
            int x;
            char ch[5];
            scanf("%d%s", &x, ch);
            if(x - 1 >= 1 && a[x - 1] > a[x])
                T1.add(x - 1, -1);
            if(x + 1 <= n && a[x] > a[x + 1])
                T1.add(x, -1);
            cnt[a[x] - 'a'].add(x, -1);
            a[x] = *ch;
            
            T2.modify(1, x, a[x] - 'a');
            cnt[a[x] - 'a'].add(x, 1);
            if(x - 1 >= 1 && a[x - 1] > a[x])
                T1.add(x - 1, 1);
            if(x + 1 <= n && a[x] > a[x + 1])
                T1.add(x, 1);
        }
        else
        {
            int l, r;
            scanf("%d%d", &l, &r);
            int ok = 1;
            if(T1.ask(r - 1) - T1.ask(l - 1) > 0) //[l, r - 1]是否递增
                ok = 0;
            int mx_ch = T2.query_mx(1, l, r);
            int mn_ch = T2.query_mn(1, l, r);
            for(int i = mn_ch + 1; i <= mx_ch - 1; i ++)
                if(cnt[i].ask(r) - cnt[i].ask(l - 1) != cnt[i].ask(n))
                    ok = 0;
            if(ok)
                cout << "Yes\n";
            else
                cout << "No\n";
        }
    }    
}

标签:Atcoder,ABC,int,题解,add,T1,休息日,ch,升序
From: https://www.cnblogs.com/DM11/p/17058870.html

相关文章

  • 【题解】P4768 [NOI2018] 归程(树上倍增,Kruskal 重构树,dijkstra)
    【题解】P4768[NOI2018]归程作为一道NOI的D1T1,放在现在或许难度不够(?),但可能在Kruskal重构树还未普及的当时,还是相对来说比较难的一道题吧。写篇题解记录一下。题......
  • P5020 [NOIP2018 提高组] 货币系统 题解
    注意:此题题解写的较为简略。P5020[NOIP2018提高组]货币系统转化为完全背包即可。#include<iostream>#include<cstring>#include<algorithm>usingnamespaces......
  • P1941 [NOIP2014 提高组] 飞扬的小鸟 题解
    WC-2023上的题目。线性动态规划P1941[NOIP2014提高组]飞扬的小鸟我们先不管障碍物。设\(f[i][j]\)表示来到点\((i,j)\)的最少点击屏幕数。因为每秒要不上升\(......
  • P1880 [NOI1995] 石子合并 题解
    P1880[NOI1995]石子合并区间DP。首先将其复制一遍(因为是环)。设\(f[i][j]\)表示将\(i\)到\(j\)段的石子合并需要的次数。有\[f[i][j]=0(i=j)\]\[f[i][j]......
  • 涂满它!(涂色问题 (原题:水叮当的舞步))题解
    F.涂满它!内存限制:256MiB时间限制:1000ms标准输入输出题目类型:传统评测方式:文本比较题目描述Flood-it是谷歌+平台上的非常好玩的一款游戏,游戏界面如下所示:在......
  • 2023牛客寒假算法基础集训营1题解
    写在前面全文收集了部分学长以及我自己的代码,本蒟蒻第一次写博客,效果不好请见谅TwT原题链接:https://ac.nowcoder.com/acm/contest/46800#questionA:WorldFinal?WorldC......
  • 洛谷普及组模拟赛 题解报告
    洛谷普及组模拟赛题解报告\[\bf{Prepared\by\InoueTakina.}\]前言:祝大家身体健康。本场比赛较为良心,经过了多次难度平衡,应该严格低于NOIP2018提高组,相信大家......
  • AtCoder Beginner Contest 285 E(背包dp)
    E-WorkorRest题目大意:给定一周有n天,其中至少有1天为休息日,其余为工作日。同时给定一个长度为n的整型数组A,对于一个工作日,它能产生的工作值为A\(_{min(x,y)}\),其中x......
  • 洛谷P3195 玩具装箱 题解报告
    题目地址题意:如题所述。分析:斜率优化dp模板题。题目没看清就下手,自以为题面所述中i>j;原始dp式子也没设计准确。中途在错误方向上头铁时,甚至没注意到横坐标是沿......
  • 洛谷P3628 特别行动队 题解报告
    题目地址题意:把正整数序列分隔为若干区间,若单个区间的元素之和为X,则其贡献为\(aX^2+bX+c\)。求所有区间的贡献之和的最大值。分析:斜率优化dp模板题。这篇博客描述得......