CF1458D Flip and Reverse[题解]
Flip and Reverse
题目大意
给定一个 \(01\) 字符串,有机会进行若干次操作,对于每一次操作:
选择该字符串的子串,要求是该子串内包含数量相同的 \(0\) , \(1\) 字符。
将该子串内的所有字符取反, \(1\) 变成 \(0\) ,\(0\) 变成 \(1\) 。
把选中的子串顺序反转。
求经过若干次操作后字典序最小的字符串。
分析
若将 \(1\) 赋值为 \(1\) , \(0\) 赋值为 \(-1\) ,进行前缀和运算,我们能够发现该操作的含义在前缀和中就是选择前缀和相同的两个点,将这两个点之间的前缀和反转。
例如样例 \(100101\) ,前缀和为 \(0\) , \(1\) , \(0\) , \(-1\) , \(0\) , \(-1\) , \(0\) 。
若我们选择前四个字符 \(1001\) 操作,等价于将七个前缀和中第 \(1\) 到第 \(5\) 个前缀和顺序反转,最后得到的结果都会是 \(011001\)。
通过前缀和图像我们能够较为轻松的发现这个性质:

红色的线表示对称轴,绿色的线便是操作之后的前缀和,略作分析能够发现其正确性。
考虑如何求得答案,考虑贪心。我们没有必要一位一位的去操作,我们只需要考虑每一位的前缀和最小能够填几就可以了。简单的,如果目前位数的前缀和为 \(k\) ,那么下一位的前缀和有两种可能 \(k+1\) , \(k-1\) ,我们当然想填 \(k-1\) ,考虑填入 \(k-1\) 的限制条件。
考虑对于原前缀和序列,每个数向相邻的数连无向边。由于对于前缀和序列的操作是选择子串翻转,并且开头和结尾的前缀和是一样的,那么其实不难发现,不管怎么操作,两个前缀和之间边的数量是不变的。
这样连边之后,能够填入 \(k-1\) 的有两种,第一种是没有连接 \(k+1\) 的边了,那么肯定就能够走 \(k-1\) ,第二种是 \(k\) 和 \(k-1\) 的连边至少有两条。
接下来说明为什么 \(k\) 和 \(k-1\) 的连边为什么至少需要两条。
如果 \(k\) 和 \(k-1\) 的连边只有一条,如果 \(k\) 后面直接就跟 \(k-1\) ,那么这种情况是肯定可以填的,但是由于我们已经知道了 \(k-1\) 和 \(k\) 的连边只有一条,所以最后不管怎么变化,前缀和不可能再回到 \(k\) ,自然也不会存在 \(k\) 与 \(k+1\) 的连边,则其实第一种情况就已经特判掉了只有一条连边时的情况。
那么 \(k\) 和 \(k-1\) 的连边有两条为什么一定对呢?首先后面原本就是 \(k-1\) 的情况当然是可以的,但是如果后面是 \(k+1\) ,要满足至少有两条与 \(k-1\)的连边,则必须满足如下图的前缀和变化:
而排除掉第一种特殊情况,若只有一条连边的情况则如下图:
发现限制条件后,就可以贪心的往下填,最后求出的答案即是最优解。
CODE
#include <bits/stdc++.h>
using namespace std;
const int N=1e6+10;
int n;
char s[N];
int a[N],pre[N];
//edge[0/1][0/1][i]中第一位表示增减连边,第二位表示目前数值i的正负
int edge[2][2][N];
inline int read()
{
int s=0,w=1;
char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();}
while(ch>='0'&&ch<='9') s=s*10+ch-'0',ch=getchar();
return s*w;
}
int main()
{
n=read();
while(n--){
scanf("\n%s",s+1);
int len=strlen(s+1);
for(register int i=1;i<=len;i++) a[i]=s[i]-'0';
for(register int i=1;i<=len;i++){ //处理前缀和
if(a[i]==1) pre[i]=pre[i-1]+1;
else pre[i]=pre[i-1]-1;
}
for(register int i=0;i<len;i++){ //处理出前缀和与边的数量f
int sym1= pre[i]>=0 ? 1 : 0,sym2= pre[i+1]>=0 ? 1 : 0;
int change= pre[i+1]>pre[i] ? 1 : 0;
edge[change][sym1][abs(pre[i])]++,edge[change^1][sym2][abs(pre[i+1])]++;
}
int now=0;
for(register int i=1;i<=len;i++){ //考虑每一位如何填充
int sym1= now>=0 ? 1 : 0;
if(edge[0][sym1][abs(now)]>=2||!edge[1][sym1][abs(now)]){
printf("0"),now--;
int sym2= now>=0 ? 1 : 0;
edge[0][sym1][abs(now+1)]--,edge[1][sym2][abs(now)]--;
}
else{
printf("1"),now++;
int sym2= now>=0 ? 1 : 0;
edge[1][sym1][abs(now-1)]--,edge[0][sym2][abs(now)]--;
}
}
printf("\n");
}
return 0;
}
CF1458D Flip and Reverse[题解]的更多相关文章
- [cf1458D]Flip and Reverse
将$s$中的01分别变为$1,-1$,即得到一个序列$a_{i}$(设其长度为$n$,下标范围为$[1,n]$) 对$a_{i}$建立一张有向图,其点集合为$Z$,并对$\forall 0\le k& ...
- 多校联训 DS 专题
CF1039D You Are Given a Tree 容易发现,当 \(k\) 不断增大时,答案不断减小,且 \(k\) 的答案不超过 \(\lfloor\frac {n}{k}\rfloor\) ...
- 小白学jquery Mobile《构建跨平台APP:jQuery Mobile移动应用实战》连载四(场景切换)
作为一款真正有使用价值的应用,首先应该至少有两个页面,通过页面的切换来实现更多的交互.比如手机人人网,打开以后先是进入登录页面,登录后会有新鲜事,然后拉开左边的面板,能看到相册.悄悄话.应用之类的其他 ...
- bzoj 2631: tree 动态树+常数优化
2631: tree Time Limit: 30 Sec Memory Limit: 128 MBSubmit: 1716 Solved: 576[Submit][Status] Descrip ...
- 白学jquery Mobile《构建跨平台APP:jQuery Mobile移动应用实战》串行4(场景变化)
作为一个真正的利用价格值应用,首先,你应该至少有两页,通过切换页面来实现很多其他互动.比如手机人人网,首先,打开后进入登录页面,将有登录后,新的东西.然后拉左侧面板.你可以看到相册.私人信息.像其他应 ...
- css3 翻牌动画
最近做了一个特效,css是从网上找的,地址是这个: CSS3 animate flip下的纸牌翻转效果实例页面 把其中核心的css代码扒出来如下: /* The properties in this ...
- MOG插件(葡萄牙语,略作翻译)
这次记录下MOG大神的插件,自从我发现了这个插件,似乎开启了一个新世界诶~~~ 网址 https://atelierrgss.wordpress.com 1. MOG_YuruYuri.js CARA ...
- WebApp之H5登录注册
代码indexhtml <!DOCTYPE html> <html> <head> <meta charset="utf-8"> & ...
- 平衡树 & LCT
1. 非旋 Treap(FHQ Treap) 1.1. 算法简介 FHQ Treap 的功能非常强大.它涵盖了 Treap 几乎所有的功能 所以我非常后悔学了 Treap,浪费时间. FHQ 的核心思 ...
随机推荐
- 再见Xshell、Xftp!Python执行Linux命令、上传下载远程文件
相信大家应该都接触过Linux操作系统(Ubuntu.Centos等),那么在使用的Linux操作系统需要使用一些远程ssh工具,尤其是公网服务器. 常用的ssh工具主要有:Xshell.MobaXt ...
- GO学习-(9) Go语言基础之切片
Go语言基础之切片 本文主要介绍Go语言中切片(slice)及它的基本使用. 引子 因为数组的长度是固定的并且数组长度属于类型的一部分,所以数组有很多的局限性. 例如: func arraySum(x ...
- THINKPHP_(7)_THINKPHP6的controller模型接收前端页面通过ajax返回的数据,会因为一个div而失败
这个随笔比较短. 同样的前端页面代码,修改了一下,后端模型接收不到数据. 利用beyond compare软件比对两个前端文件, 发现多了一个</div>标签. 多了一个</div& ...
- Java真的是白天鹅
前言 我最近越来越真切的感受到,Java真的是白天鹅. 这真的是一种羡慕嫉妒恨的感受. 今天和一个Java技术Leader聊天,我告诉他敏捷开发是以人为本,他居然跟我说敏捷开发在行业内有规范,规范是死 ...
- OpenFeign远程调用原理
之前对OpenFeign 了解到只用在接口上面打个注解,然后就可以通过内部调用去调用远程地址.研究完Feign生成对象以及代理对象的作用过程之后发现这个过程用到了Spring的好多东西,在之后的过程中 ...
- UE4.22编辑器界面操控设置(4)
视频课程地址:https://i.youku.com/i/UMzE2NDk2OTIw/custom?spm=a2hzp.8244740.0.0&id=32318 -在场景中按住鼠标左键上下移动 ...
- 性能工具之stress工具使用教程(带源码说明)
stress是一个在linux下的压力测试小工具. 我看到有些人用这个工具来描述一些资源耗尽的场景,也有人用它来做混沌测试中.请使用者要注意,这个工具并不是模拟业务问题的,是模拟系统级问题的.所以 ...
- redis学习第一天
不同于其他的常用关系型数据库,redis是一个非常轻便,体积小,存放键值对的数据库,常用于构建高性能,可扩展的Web应用程序. 这是我第一次接触redis,之前没有使用过,只听说过.因为刚毕业,找工作 ...
- python读取csv文件数据绘制图像,例子绘制天气每天最高最低气温气象图
- Kali下切换JDK版本
Kali下自由更换JDK版本 今天在学习了一下CobaltStrike之后,打算在Kali上本地搭建一个服务器端, 查看了一下Kali的JDK配置发现版本是13.X的,然而CobaltStrike最好 ...