Codeforces Round #618 (Div. 2)

Codeforces Round #618 (Div. 2)

有一说一,感觉这套题的难度跟之前的div2不在一个层次上emm…
应该是把难题都放在div1了…

不过这跟我菜又有什么关系


A
先把所有的0都变成1,同时更新sum,这样子做花费最小也不会在之后的和不为0的变化上有额外的花费;之后考虑和为0的情况,如果之前没有0那么原序列一定有正数,对其操作一次即可;如果之前的序列有0并且置为1后sum为0,那么新得到的1就是一个可以操作的整数,加上一次即可。所以只要和为0,只需要操作一次即可。

B
对原序列排序,取中间两个做差输出即可。
如果想取其他两个数做差,那么由中位数的对称性质可知一定不能够构造成功…

C
注意到函数f(x, y) = x & (~y)的性质,而在迭代的过程只有第一个元素是不变的,其余所有元素各位都取反后与上之前的数值,所以很容易想到枚举每一位,用前缀与和后缀与求出这种情况下的最终函数数值;不断更新取最大值即可。


补题

D
好气啊…昨晚其实已经发现了只要这个图形是一个中心对称图形即可…
但是因为被平行四边形蒙蔽了双眼,以为只需要判断对边相等即可…WA5;后来改了下以为可以等价地判断对边平行…WA4
事实上多边形并不满足这个判断条件…判断中心对称可以直接判断所有对点的中点是否一致

当时因为两个函数都写好了本来想着两个条件 & 起来再输出,后来好像因为我判断的是存在不满足的情况就break输出NO…不高兴改了就算了…然后就干坐着调试到结束…可惜…

E
也不算难题…但是好像确实是我的知识盲区。

单调栈

最开始想的时候总想用线段树做,不过发现不好弄。不过容易发现对于当前考虑的点来说,只需要找到那个使得它均值最小的位置,然后就可以类似双指针地前进了…然而如何动态地维护一段段的区间…

tutorial用前缀和sum数组在平面上的坐标(i, sum[i])来解释一次operation:对区间(i, j]操作就是在平面上将点i和点j连线,中间的点在x为整数时取对应纵值即可。那么要使得字典序最小,就应该找这些点的最低的一个凸包(形象的hull!)。

其实凸包的解释和之前“类双指针”的解释本质相同,但是用凸包解释更加直观易懂一些。

那么最低的凸包可以用单调栈进行寻找。

const int maxn = 1e6 + 10;
int a[maxn]; ll sum[maxn];
int st[maxn], top = 0;

int main(){
//    Fast;
    int n; scanf("%d", &n);
    for(int i = 1; i <= n; i++){
        scanf("%d", a + i);
        sum[i] = sum[i - 1] + a[i];
    }
    st[top++] = 0;
    for(int i = 1; i <= n; i++){
        while(top >= 2 && (sum[i] - sum[st[top - 1]]) * (i - st[top - 2]) <= (sum[i] - sum[st[top - 2]]) * (i - st[top - 1]))
            top--;
        st[top++] = i;
    }
    int pre; pre = 0;
    double res;
    for(int i = 0; i < top; i++){
        res = (sum[st[i]] - sum[pre]) * 1.0 / (st[i] - pre);
        for(int j = pre + 1; j <= st[i]; j++) printf("%.12f\n", res);
        pre = st[i];
    }
    return 0;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值