Codeforces Round #823 (Div. 2)

A.Planets

题意:

n n n个星球,每个星球属于一个星系 b i b_i bi,每次可以花费 1 1 1的代价摧毁一个星球,也可以花费 c c c的代价摧毁一个星系里的所有星球,问把所有星球摧毁的最小代价.

思路:

贪心。统计每个星系各有多少星球,若星球数大于 c c c,则选择花费 c c c,否则逐一摧毁即可。

复杂度 O ( n ) \Omicron(n) O(n)

B.Meeting on the Line

题意:

在数轴上住着 n n n个人,第 i i i个人房子的坐标为 x i x_i xi.现在ta们要参加一场聚会,第 i i i个人会花 t i t_i ti的时间进行化妆,所以如果聚会坐标选在 x x x,那么第 i i i人将在时刻 t i + ∣ x i − x ∣ t_i+|x_i-x| ti+xix到达.所有人到达时聚会开始。求合适的 x x x,使得聚会尽可能早地开始.

思路:

先将这 n n n个人按照坐标从小到大排序,考虑到最终地点若满足 x i ≤ x ≤ x i + 1 x_i \leq x \leq x_{i+1} xixxi+1,那么对于任意的 j ≤ i j \leq i ji,到达的时刻为 x − x j + t j x-x_j+t_j xxj+tj,最晚到达时刻为 m a x { t j − x j } + x max \{ t_j-x_j \}+x max{tjxj}+x;对于任意的 j > i j>i j>i,到达时刻为 x j − x + t j x_j-x+t_j xjx+tj,最晚到达时刻为 m a x { x j + t j } − x max \{x_j+t_j\}-x max{xj+tj}x。因此我们先求出前缀最大值 l m x i = m a x { t k − x k } , k ≤ i lmx_i=max \{t_k-x_k\},k\leq i lmxi=max{tkxk},ki和后缀最大值 r m x i = m a x { t k + x k } , k ≥ i rmx_i=max\{t_k+x_k\},k\geq i rmxi=max{tk+xk},ki.

由此,在 i i i i + 1 i+1 i+1个人之间,我们需要在 [ x i , x i + 1 ] [x_i,x_{i+1}] [xi,xi+1]之间选定一个 x x x,使得两函数 y = l m x i + x y=lmx_i+x y=lmxi+x y = r m x i + 1 − x y=rmx_{i+1}-x y=rmxi+1x中最大值最小,线性规划即可。具体操作细节详见代码.

code
ll T;

ll n;
struct node {
    ll pos, t;
}p[N];
inline bool cmp(node a, node b) {
    return a.pos<b.pos;
}

ll lmx[N], rmx[N];
ll val1[N], val2[N];
dl mx, res;
int main () {
    read(T);
    while (T--) {
        read(n);
        for (R ll i=1; i<=n; i++) read(p[i].pos);
        for (R ll i=1; i<=n; i++) read(p[i].t);
        sort(p+1, p+n+1, cmp);
        if (p[1].pos==p[n].pos) {
            writeln(p[1].pos);
            continue;
        }
        lmx[0]=rmx[n+1]=-1234568779;
        for (R ll i=1; i<=n; i++) {
            lmx[i]=max(lmx[i-1], p[i].t-p[i].pos);
            }
        for (R ll i=n; i; i--) {
            rmx[i]=max(rmx[i+1], p[i].pos+p[i].t);
        }
        mx=13645654456655.0;
        for (R ll i=1; i<n; i++) {
            dl rem=mx;
            if (lmx[i]+p[i].pos>rmx[i+1]-p[i].pos) {
                mx=min(mx, (dl)(lmx[i]+p[i].pos));
                if (mx<rem) res=(dl)p[i].pos;
            }else if (lmx[i]+p[i+1].pos<rmx[i+1]-p[i+1].pos) {
                mx=min(mx, (dl)(rmx[i+1]-p[i+1].pos));
                if (mx<rem) res=(dl)p[i+1].pos;
            }else {
                mx=min(mx, (dl)(lmx[i]+rmx[i+1])/2.0);
                if (mx<rem) res=(dl)(rmx[i+1]-lmx[i])/2.0;
            }
        }
        printf("%.8lf\n", res);
}
}
C.Minimum Notation

题意:

给定一个长为 n n n数字串 s s s,可以进行如下操作:

选定第 i i i个数 a i a_i ai,将 a i a_i ai删除,同时获得一个新数 m i n ( a i + 1 , 9 ) min(a_i+1, 9) min(ai+1,9),你必须把它插入到序列中某一位置,或放置在末尾.

求经过任意多次操作后能够得到的最小字典序数字串.

思路:

贪心地想,显然每次选取最小的值并将其之间的所有数清空可以使得前缀字典序最小。然后将清空后的数中较小的补齐,接着进行下一轮操作.

code
const ll N=2e5+5;

char s[N];
ll T;
ll n, num[N];
ll tot[N], now;

inline ll find(ll x) {
    ll d=10;
    for (R ll i=x; i<=n; i++) chkmin(d, num[i]);
    return d;
}

inline ll findr(ll x) {
    for (R ll i=n; i; i--) {
        if (num[i]==x) return i;
    }
}

inline void work(ll l, ll r, ll k) {
    for (R ll i=0; i<=k; i++) {
        while (tot[i]) write(i), tot[i]--;
    }
    for (R ll i=l; i<=r; i++) {
        if (num[i]!=k) {
            if (num[i]==9) ++tot[9];
            else ++tot[num[i]+1];
        }else write(k);
    }
}

int main () {
    read(T);
    while (T--) {
        scanf("%s", s+1);
        n=strlen(s+1);
        for (R ll i=1; i<=n; i++) num[i]=s[i]-'0';
        now=1;
        while (now<=n) {
            ll d=find(now), nowr=findr(d);
            work(now, nowr, d); now=nowr+1;
        }
        for (R ll i=0; i<10; i++) {
            while (tot[i]) write(i), --tot[i];
        }
        putchar('\n');
    }
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值