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;
}