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[题解]的更多相关文章

  1. [cf1458D]Flip and Reverse

    将$s$中的01分别变为$1,-1$,即得到一个序列$a_{i}$(设其长度为$n$,下标范围为$[1,n]$) 对$a_{i}$建立一张有向图,其点集合为$Z$,并对$\forall 0\le k& ...

  2. 多校联训 DS 专题

    CF1039D You Are Given a Tree 容易发现,当 \(k\) 不断增大时,答案不断减小,且 \(k\) 的答案不超过 \(\lfloor\frac {n}{k}\rfloor\) ...

  3. 小白学jquery Mobile《构建跨平台APP:jQuery Mobile移动应用实战》连载四(场景切换)

    作为一款真正有使用价值的应用,首先应该至少有两个页面,通过页面的切换来实现更多的交互.比如手机人人网,打开以后先是进入登录页面,登录后会有新鲜事,然后拉开左边的面板,能看到相册.悄悄话.应用之类的其他 ...

  4. bzoj 2631: tree 动态树+常数优化

    2631: tree Time Limit: 30 Sec  Memory Limit: 128 MBSubmit: 1716  Solved: 576[Submit][Status] Descrip ...

  5. 白学jquery Mobile《构建跨平台APP:jQuery Mobile移动应用实战》串行4(场景变化)

    作为一个真正的利用价格值应用,首先,你应该至少有两页,通过切换页面来实现很多其他互动.比如手机人人网,首先,打开后进入登录页面,将有登录后,新的东西.然后拉左侧面板.你可以看到相册.私人信息.像其他应 ...

  6. css3 翻牌动画

    最近做了一个特效,css是从网上找的,地址是这个: CSS3 animate flip下的纸牌翻转效果实例页面 把其中核心的css代码扒出来如下: /* The properties in this ...

  7. MOG插件(葡萄牙语,略作翻译)

    这次记录下MOG大神的插件,自从我发现了这个插件,似乎开启了一个新世界诶~~~ 网址 https://atelierrgss.wordpress.com 1. MOG_YuruYuri.js CARA ...

  8. WebApp之H5登录注册

    代码indexhtml <!DOCTYPE html> <html> <head> <meta charset="utf-8"> & ...

  9. 平衡树 & LCT

    1. 非旋 Treap(FHQ Treap) 1.1. 算法简介 FHQ Treap 的功能非常强大.它涵盖了 Treap 几乎所有的功能 所以我非常后悔学了 Treap,浪费时间. FHQ 的核心思 ...

随机推荐

  1. 性能调优命令之jstack

    jstack是java虚拟机自带的一种线程堆栈跟踪工具. /opt/java8/bin/jstack Usage: jstack [-l] <pid> (to connect to run ...

  2. Step By Step(Lua开篇)

    Step By Step(Lua开篇) 一.简介: Lua作为目前最为流行的.免费轻量级嵌入式脚本语言,在很多工业级的应用程序中被广泛应用,如Adobe's Photoshop,甚至是在一些著名的游戏 ...

  3. eclipse集成processing、PApplet、proclipsing 问题

    最近老是换应用平台,将processing里的代码转移到eclipse中. processing 关于转换成eclipse的介绍也可以使用,但是没有介绍具体怎么使用第三方库 Processing in ...

  4. 如何使用TVM Pass红外线

    如何使用TVM Pass红外线 随着Relay / tir中优化遍数的增加,执行并手动维护其依赖关系变得很棘手.引入了一个基础结构来管理优化过程,将其应用于TVM堆栈中IR的不同层. Relay / ...

  5. C++标准模板库(STL)——map常见用法详解

    map的定义 map<typename1, typename2> mp; map需要确定映射前类型和映射后类型,所以需要在<>内填写两个类型,第一个是键的类型,第二个是值的类型 ...

  6. Nginx虚拟主机流量状态模块(nginx-module-vts)使用说明文档(四)

    装完NG,为了拿到各种状态指标,就要对NG做监控. Github 2.3k的开源项目nginx-module-vts没准真是你需求的. 链接数,qps,1xx.2xx,.3xx.4xx.5xx的响应数 ...

  7. python operator操作符函数

    本模块主要包括一些Python内部操作符对应的函数.这些函数主要分为几类:对象比较.逻辑比较.算术运算和序列操作.

  8. Reactive 理解 SpringBoot 响应式的核心-Reactor

    Reactive 理解 SpringBoot 响应式的核心-Reactor bestcoding 2020-02-23 17:26:43 一.前言 关于 响应式 Reactive,前面的两篇文章谈了不 ...

  9. AVAssetWriter视频数据编码

    AVAssetWriter介绍 可以通过AVAssetWriter来对媒体样本重新做编码. 针对一个视频文件,只可以使用一个AVAssetWriter来写入,所以每一个文件都需要对应一个新的AVAss ...

  10. 干货 | LuatOS BSP移植教程,简单到复制粘贴!!!

    LuatOS本着自身的开源特性,可以很轻松的嵌入到很多微处理器和微控制器.今天简要讲下如何移植这套系统,上手比较简单,看完基本就会了. 要想做移植,就要先了解需要移植芯片的SDK,LuatOS依赖于F ...