Codeforces Round #466 (Div. 2) 题解

人生中第三次 CF C F 。。。
考试中切了 A A ~E
F F 题会做没时间写


题解

A:Points on the line

题意

给定一个数列,删最小的数,使最大差不大于一个定值

Sol

排序后选的一定是段连续的区间,枚举左右端点即可

手速慢了233

# include <bits/stdc++.h>
# define RG register
# define IL inline
# define Fill(a, b) memset(a, b, sizeof(a))
using namespace std;
typedef long long ll;
const int _(105);

IL int Input(){
    RG int x = 0, z = 1; RG char c = getchar();
    for(; c < '0' || c > '9'; c = getchar()) z = c == '-' ? -1 : 1;
    for(; c >= '0' && c <= '9'; c = getchar()) x = (x << 1) + (x << 3) + (c ^ 48);
    return x * z;
}

int n, d, x[_], ans;

int main(RG int argc, RG char* argv[]){
    n = Input(), d = Input();
    for(RG int i = 1; i <= n; ++i) x[i] = Input();
    sort(x + 1, x + n + 1);
    if(!n || x[n] - x[1] <= d) return puts("0"), 0;
    for(RG int i = 1; i <= n; ++i)
        for(RG int j = i; j <= n; ++j)
            if(x[j] - x[i] <= d) ans = max(ans, j - i + 1);
    printf("%d\n", n - ans);
    return 0;
}

B:Our Tanya is Crying Out Loud

题意

给定数n k k 以及代价A B B
你可以花A的代价给 n n 1
或者当 n n k的倍数时花 B B 的代价把n变为 n/k n / k
n n 变为1的最小代价

Sol

不是 k k 的倍数,就减到k的倍数为止
否则比较减和除的代价,取最小

# include <bits/stdc++.h>
# define RG register
# define IL inline
# define Fill(a, b) memset(a, b, sizeof(a))
using namespace std;
typedef long long ll;
const int _(105);

IL int Input(){
    RG int x = 0, z = 1; RG char c = getchar();
    for(; c < '0' || c > '9'; c = getchar()) z = c == '-' ? -1 : 1;
    for(; c >= '0' && c <= '9'; c = getchar()) x = (x << 1) + (x << 3) + (c ^ 48);
    return x * z;
}

int n, k, a, b;
ll ans = 0;

int main(RG int argc, RG char* argv[]){
    n = Input(), k = Input(), a = Input(), b = Input();
    if(k == 1){
        cout << 1LL * a * (n - 1) << endl;
        return 0;
    }
    while(n >= k){
        RG int t = n / k, tt = n % k;
        n -= tt;
        ans += 1LL * tt * a;
        ans += min(1LL * b, 1LL * (n - t) * a);
        n = t;
    }
    ans += 1LL * a * (n - 1);
    cout << ans << endl;
    return 0;
}

C:Phone Numbers

题意

一个长度为 n n 的小写字母组成的字符串和一个整数k,要你生成一个最小字典序的长度为 k k 的小写字母组成的字符串
使该字符串的字符集为给定串的字符集的子集,并且字典序大于给定串

Sol

找到一个最后面的位置可以使答案串大于原串,后面直接填最小的字符

# include <bits/stdc++.h>
# define RG register
# define IL inline
# define Fill(a, b) memset(a, b, sizeof(a))
using namespace std;
typedef long long ll;
const int _(1e5 + 5);

IL int Input(){
    RG int x = 0, z = 1; RG char c = getchar();
    for(; c < '0' || c > '9'; c = getchar()) z = c == '-' ? -1 : 1;
    for(; c >= '0' && c <= '9'; c = getchar()) x = (x << 1) + (x << 3) + (c ^ 48);
    return x * z;
}

int n, k, t[30], pos, g[30];
char s[_];

int main(RG int argc, RG char* argv[]){
    n = Input(), k = Input();
    scanf(" %s", s + 1), s[n + 1] = 'a' - 1;
    for(RG int i = 1; i <= n; ++i) ++t[s[i] - 'a'];
    for(RG int i = 25; ~i; --i) g[i] = t[i], t[i] += t[i + 1];
    for(RG int i = min(k, n + 1); i; --i)
        if(t[s[i] - 'a' + 1]){
            pos = i;
            break;
        }
    for(RG int i = 1; i < pos; ++i) printf("%c", s[i]);
    for(RG int i = s[pos] - 'a' + 1; i < 26; ++i)
        if(g[i]){
            printf("%c", i + 'a');
            break;
        }
    RG int tmp = 0;
    for(RG int i = 0; i < 26; ++i)
        if(g[i]){
            tmp = i;
            break;
        }
    for(RG int i = pos + 1; i <= k; ++i) printf("%c", tmp + 'a');
    return 0;
}

D:Alena And The Heater

题意

给定数列A
以及生成数列 B B 的条件:
B[1]=B[2]=B[3]=B[4]=0
对于 i>4 i > 4
如果 a[i],a[i1],a[i2],a[i3],a[i4]>r a [ i ] , a [ i − 1 ] , a [ i − 2 ] , a [ i − 3 ] , a [ i − 4 ] > r b[i1]=b[i2]=b[i3]=b[i4]=1 b [ i − 1 ] = b [ i − 2 ] = b [ i − 3 ] = b [ i − 4 ] = 1 b[i]=1 b [ i ] = 1
如果 a[i],a[i1],a[i2],a[i3],a[i4]<l a [ i ] , a [ i − 1 ] , a [ i − 2 ] , a [ i − 3 ] , a [ i − 4 ] < l b[i1]=b[i2]=b[i3]=b[i4]=0 b [ i − 1 ] = b [ i − 2 ] = b [ i − 3 ] = b [ i − 4 ] = 0 b[i]=0 b [ i ] = 0
否则 b[i]=b[i1] b [ i ] = b [ i − 1 ]
保证一定有解,输出 l,r l , r 可以构造出给定的数列 B B
输出任意一组l,r[1e9,1e9]

Sol

保证一定有解,那么按题意反过来构造 l,r l , r 即可

# include <bits/stdc++.h>
# define RG register
# define IL inline
# define Fill(a, b) memset(a, b, sizeof(a))
using namespace std;
typedef long long ll;
const int _(1e5 + 5);

IL int Input(){
    RG int x = 0, z = 1; RG char c = getchar();
    for(; c < '0' || c > '9'; c = getchar()) z = c == '-' ? -1 : 1;
    for(; c >= '0' && c <= '9'; c = getchar()) x = (x << 1) + (x << 3) + (c ^ 48);
    return x * z;
}

int n, a[_], b[_], l = -1e9, r = 1e9;
char s[_];

int main(RG int argc, RG char* argv[]){
    n = Input();
    for(RG int i = 1; i <= n; ++i) a[i] = Input();
    scanf(" %s", s + 1);
    for(RG int i = 1; i <= n; ++i) b[i] = s[i] - '0';
    for(RG int i = 5; i <= n; ++i){
        RG int s = b[i - 1] + b[i - 2] + b[i - 3] + b[i - 4];
        if(s && s != 4) continue;
        if(b[i] == b[i - 1]) continue;
        if(b[i]){
            RG int mx = -1e9;
            for(RG int j = 0; j <= 4; ++j) mx = max(mx, a[i - j]);
            l = max(l, mx + 1);
        }
        else{
            RG int mn = 1e9;
            for(RG int j = 0; j <= 4; ++j) mn = min(mn, a[i - j]);
            r = min(r, mn - 1);
        }
    }
    printf("%d %d\n" ,l, r);
    return 0;
}

E:Cashback

题意

给定一个长度为 n n 的数列以及一个整数c
一个下标在 [l,r] [ l , r ] 内的子数列的价值为所有数的和减去子数列中前 rl+1c ⌊ r − l + 1 c ⌋ 小的数的和
求把这个数列分成若干个块,使的每块的价值和最小

Sol

k k 小?离散化+主席树辣
码完
发现只会n2的滴劈
f[i] f [ i ] 表示做到第 i i 个数的最小代价,枚举长度转移,前k小的和主席树查询
你当这是 OI O I 赛制啊?没有部分分的。。
我们分情况考虑

  • 若分一块长度小于 c c
    价值就是所有的数的和
    那么还不如一个一个选更方便转移
  • 若分一块长度为c的倍数的
    我们把它分成若干个长度为 c c 的块,就是总和减去每个块的最小值的和
    而直接分一块就是总和减去整个块的前几个最小值
    整个块的前几个最小值一定小于等于若干块的最小值和
    那么分成若干个长度为c的块最优
  • 若一块长度大于等于 c c 的,就是上面两种组合而来

那么策略就是:要么一个一个选,要么选c个一块
只要求区间最小值, RMQ R M Q 问题
然而主席树懒得删

# include <bits/stdc++.h>
# define RG register
# define IL inline
# define Fill(a, b) memset(a, b, sizeof(a))
using namespace std;
typedef long long ll;
const int _(1e5 + 5);

IL int Input(){
    RG int x = 0, z = 1; RG char c = getchar();
    for(; c < '0' || c > '9'; c = getchar()) z = c == '-' ? -1 : 1;
    for(; c >= '0' && c <= '9'; c = getchar()) x = (x << 1) + (x << 3) + (c ^ 48);
    return x * z;
}

int n, c, tot, len, rt[_];
ll f[_], s[_], o[_], a[_];
struct HJT{
    int sz, rs, ls;
    ll sum;
} T[_ * 20];

IL void Modify(RG int &x, RG int l, RG int r, RG int id, RG ll v){
    T[++tot] = T[x], T[x = tot].sum += v, ++T[x].sz;
    if(l == r) return;
    RG int mid = (l + r) >> 1;
    if(id <= mid) Modify(T[x].ls, l, mid, id, v);
    else Modify(T[x].rs, mid + 1, r, id, v);
}

IL ll Query(RG int A, RG int B, RG int l, RG int r, RG int k){
    if(l == r) return o[l];
    RG int mid = (l + r) >> 1, ss = T[T[B].ls].sz - T[T[A].ls].sz;
    RG ll sss = T[T[B].ls].sum - T[T[A].ls].sum;
    if(ss >= k) return Query(T[A].ls, T[B].ls, l, mid, k);
    else return sss + Query(T[A].rs, T[B].rs, mid + 1, r, k - ss);
}

int main(RG int argc, RG char* argv[]){
    n = Input(), c = Input(), Fill(f, 127), f[0] = 0;
    for(RG int i = 1; i <= n; ++i) o[i] = a[i] = Input(), s[i] = s[i - 1] + a[i];
    sort(o + 1, o + n + 1), len = unique(o + 1, o + n + 1) - o - 1;
    for(RG int i = 1; i <= n; ++i){
        RG int p = lower_bound(o + 1, o + len + 1, a[i]) - o;
        rt[i] = rt[i - 1], Modify(rt[i], 1, len, p, a[i]);
    }
    for(RG int i = 1; i < c; ++i) f[i] = s[i];
    for(RG int i = c; i <= n; ++i){
        RG ll sum = s[i] - s[i - c] - Query(rt[i - c], rt[i], 1, len, 1);
        f[i] = min(f[i - 1] + a[i], f[i - c] + sum);
    }
    cout << f[n] << endl;
    return 0;
}

F:Machine Learning

题意

给一个数列
每次询问一个区间 [l,r] [ l , r ]
求数列下标在这个区间内的数的个数的 mex m e x
p.s p . s mex m e x 值是最小的没出现的整数值
数会随时修改

Sol

这不就是离散化+带修改莫队吗?
吐槽一下:
mex m e x 值开桶暴力求就能过了

我的做法:
把数字也分块,计算 mex m e x 时,找到第一个不满的块,再在块内找的那个 mex m e x

还有一点就是
TM T M 学了假的带修改莫队:
没按时间为第三关键字排序!!!
没把块的大小设为 n23 n 2 3 !!!
没比较右端点所在的块!!!
然后一直 TLE T L E QAQ Q A Q

# include <bits/stdc++.h>
# define RG register
# define IL inline
# define Fill(a, b) memset(a, b, sizeof(a))
using namespace std;
typedef long long ll;
const int _(4e5 + 5);

IL int Input(){
    RG int x = 0, z = 1; RG char c = getchar();
    for(; c < '0' || c > '9'; c = getchar()) z = c == '-' ? -1 : 1;
    for(; c >= '0' && c <= '9'; c = getchar()) x = (x << 1) + (x << 3) + (c ^ 48);
    return x * z;
}

int n, q, bl[_], o[_], len, a[_], ans[_], blo, cnt, tot;
int t[_], sum[5000], size[_];
struct Query{
    int l, r, t, id;

    IL bool operator <(RG Query B) const{
        if(bl[l] != bl[B.l]) return l < B.l;
        if(bl[r] != bl[B.r]) return r < B.r;
        return t < B.t;
    }
} qry[_];
struct Modify{
    int p, x;
} mdy[_];

IL void Change(RG int x, RG int d){
    if(d > 0){
        if(!size[x]) ++sum[x / blo];
        ++size[x];
    }
    else{
        --size[x];
        if(!size[x]) --sum[x / blo];
    }
}

IL int Mex(){
    for(RG int i = 0; ; ++i)
        if(sum[i] != blo){
            for(RG int j = 0; j < blo; ++j)
                if(!size[i * blo + j]) return i * blo + j;
        }
}

IL void Calc(RG int v, RG int d){
    Change(t[v], -1), t[v] += d, Change(t[v], 1);
}

IL void Adjust(RG int j, RG int l, RG int r){
    if(mdy[j].p >= l && mdy[j].p <= r) Calc(a[mdy[j].p], -1);
    swap(a[mdy[j].p], mdy[j].x);
    if(mdy[j].p >= l && mdy[j].p <= r) Calc(a[mdy[j].p], 1);
}

int main(RG int argc, RG char* argv[]){
    n = Input(), q = Input(), blo = pow(n, 2.0 / 3.0);
    for(RG int i = 1; i <= n; ++i) o[++len] = a[i] = Input(), bl[i] = (i - 1) / blo;
    for(RG int i = 1; i <= q; ++i){
        RG int op = Input(), x = Input(), y = Input();
        if(op == 1) qry[++cnt] = (Query){x, y, tot, cnt};
        else mdy[++tot] = (Modify){x, y}, o[++len] = y;
    }
    sort(o + 1, o + len + 1), len = unique(o + 1, o + len + 1) - o - 1;
    for(RG int i = 1; i <= n; ++i)
        a[i] = lower_bound(o + 1, o + len + 1, a[i]) - o;
    for(RG int i = 1; i <= tot; ++i)
        mdy[i].x = lower_bound(o + 1, o + len + 1, mdy[i].x) - o;
    sort(qry + 1, qry + cnt + 1), blo = sqrt(len);
    for(RG int L = qry[1].l, R = qry[1].l - 1, i = 1, j = 0; i <= cnt; ++i){
        while(L < qry[i].l) Calc(a[L++], -1);
        while(L > qry[i].l) Calc(a[--L], 1);
        while(R < qry[i].r) Calc(a[++R], 1);
        while(R > qry[i].r) Calc(a[R--], -1);
        while(j < qry[i].t) Adjust(++j, L, R);
        while(j > qry[i].t) Adjust(j--, L, R);
        ans[qry[i].id] = Mex();
    }
    for(RG int i = 1; i <= cnt; ++i) printf("%d\n", ans[i]);
    return 0;
}

总结

CF C F 比赛的题还是不错的
总有一些奇奇怪怪的脑洞
话说歪果仁知道主席树和莫队算法吗?

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
YOLO高分设计资源源码,详情请查看资源内容中使用说明 YOLO高分设计资源源码,详情请查看资源内容中使用说明 YOLO高分设计资源源码,详情请查看资源内容中使用说明 YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值