一行格子,其中小于\(0\)的格子为白色,大于\(n\)的格子为黑色,中间的格子颜色由题目给出。

有一些格子需要被标记。标记按照以下规则进行:选择一个颜色\(c\),找到一个未标记的 旁边有标记点的 颜色为\(c\)的 格子,在这个格子上标记;如果找不到这样的格子,就找任意一个颜色为\(c\)的格子。

问标记完所有需要被标记的点,最少要操作多少次。

\(n\le 10^5\)


%%%gmh,dyp早就切了

想题的时候先想出假做法,写个\(O(n^2)\)的暴力交上去发现WA了,后来另外想到了正解的结论。当时不会证充分性就瞄了瞄题解,确认后自己证了下才开打。当然做法是自己的,所以可以很高兴地认为自己做出了一半。

很奇怪别人的做法都要设那么多状态显得特别阴间。

可以考虑:对于一个终止状态,如何判定它是可以得到的。

对于一个连续段,显然只要一个点被标记整个连续段都可以被标记;那问题转化成了:是否可以在每个连续段都至少标记一个点。

先特殊计算只有一个连续段的情况。

为了方便表示将题目中的两种情况称为操作一和操作二。显然一个连续段从被标记必须通过一次操作二。

第一次操作后,连续段相邻的两个位置的颜色都不能使用操作二;因为后面还有连续段所以必须要只有一种颜色的操作二被禁止了。类似地考虑,除了最后一个连续段,其它连续段操作之后都要保证存在一种颜色没有被禁止,而且后面的连续段都要包含这种颜色。

称这个没有被禁止操作二的颜色为\(c\)(记被禁止的颜色为\(\overline c\))。将以上的分析整理一下得:

  1. 中间的连续段算上相邻两格一定包含子序列\(\overline c c\overline c\)。
  2. 第一个连续段算上相邻两格一定包含子序列\(\overline c \overline c\)。
  3. 最后一个连续段一定包含子序列\(c\)。

于是就可以DP了。

DP方式多样,这里是阳间的:先题目的序列前后各自加两位。设\(f_{i,0/1,0/1}\)表示\(i\)不选,是否出现第一个连续段,是否出现最后一个连续段。转移分三种情况从\(f_j\)转移到\(f_i\)。可以维护每种情况的合法的最优\(f_j\),记多几个变量辅助转移。对于每种转移,显然能转移到\(i\)的是一段前缀,并且\(i\)增加时前缀伸长,于是可以预处理能转移到\(i\)的前缀的位置(子序列自动机即可)。时间就是\(O(n)\)的。


using namespace std;
#include <cstdio>
#include <cstring>
#include <algorithm>
#define N 100007
#define INF 0x7f7f7f7f
int n;
int s[N],t[N];
int ps[N][2];
int _pre[N][2];
int pre(int x,int c){return x<0?-1:_pre[x][c];}
void init(){
static char str[N];
scanf("%d%s",&n,str+1);
for (int i=1;i<=n;++i) s[i+1]=(str[i]=='b'?1:0);
scanf("%s",str+1);
for (int i=1;i<=n;++i) t[i+1]=(str[i]=='o'?1:0);
n+=3;
s[1]=0,s[n-1]=s[n]=1;
t[1]=t[n-1]=t[n]=0;
_pre[0][0]=_pre[0][1]=-1;
for (int i=1;i<=n+1;++i){
_pre[i][0]=(s[i-1]==0?i-1:_pre[i-1][0]);
_pre[i][1]=(s[i-1]==1?i-1:_pre[i-1][1]);
}
}
int ans;
int dp[N][2][2];
int rf[N],rl[N],rn[N];
int fir[2],lst[2],nor[2][2];
void upd(int &x,int y){x>y?x=y:0;}
int work(int c){
int _c=c^1;
for (int i=1;i<=n;++i){
rf[i]=pre(pre(i+1,_c)-1,_c);
rl[i]=pre(i,c)-1;
rn[i]=pre(pre(pre(i+1,_c),c),_c);
}
memset(dp,127,sizeof dp);
memset(fir,127,sizeof fir);
memset(lst,127,sizeof lst);
memset(nor,127,sizeof nor);
dp[0][0][0]=0;
int pf=0,pl=0,pn=0;
for (int i=1;i<=n;++i){
if (t[i])
continue;
for (;pf<=rf[i];++pf){
upd(fir[0],dp[pf][0][0]-pf);
upd(fir[1],dp[pf][0][1]-pf);
}
for (;pl<=rl[i];++pl){
upd(lst[0],dp[pl][0][0]-pl);
upd(lst[1],dp[pl][1][0]-pl);
}
for (;pn<=rn[i];++pn){
upd(nor[0][0],dp[pn][0][0]-pn);
upd(nor[1][0],dp[pn][1][0]-pn);
upd(nor[0][1],dp[pn][0][1]-pn);
upd(nor[1][1],dp[pn][1][1]-pn);
}
memcpy(dp[i],dp[i-1],sizeof dp[i]);
upd(dp[i][0][0],nor[0][0]+i-1);
upd(dp[i][1][0],min(nor[1][0],fir[0])+i-1);
upd(dp[i][0][1],min(nor[0][1],lst[0])+i-1);
upd(dp[i][1][1],min(nor[1][1],min(fir[1],lst[1]))+i-1);
}
return dp[n][1][1];
}
int main(){
init();
int mn=n,mx=1;
for (int i=1;i<=n;++i)
if (t[i]==1){
mn=min(mn,i);
mx=max(mx,i);
}
ans=mx-mn+1;
ans=min(ans,work(0));
ans=min(ans,work(1));
printf("%d\n",ans);
return 0;
}

ARC109F - 1D Kingdom Builder的更多相关文章

  1. [atARC109F]1D Kingdom Builder

    考虑最终有石子的位置的状态,判断一种状态是否可行 反过来,依次删除石子,删除条件是:当删除的石子是该段最后一个(即其两边都没有石子了),要求除其以外,每个连续段旁边的两个点都与其颜色不同 构造一种删除 ...

  2. [atARC109E]1D Reversi Builder

    归纳每一次操作后必然是两个颜色相同的连续段(即ww...bb...或bb...ww...),对操作的位置分类讨论不难证明正确性 当$c_{1}=c_{n}$,由于端点颜色不会修改,再根据该结论,可以得 ...

  3. AtCoder Regular Contest 109

    Contest Link 为什么还没有 Official Editorial 啊--哦,原来是日文题解,那没事了. A - Hands 有两幢 100 层的楼房 \(A,B\) ,将地面所在的楼层称为 ...

  4. TDictionary 是delphi用的,c++builder用起来太吃力。

    TDictionary 是delphi用的,c++builder用起来太吃力.c++还是用std::map代替.c++d map很好用啊.https://blog.csdn.net/ddkxddkx/ ...

  5. 23种设计模式--建造者模式-Builder Pattern

    一.建造模式的介绍       建造者模式就是将零件组装成一个整体,用官方一点的话来讲就是将一个复杂的构建与其表示相分离,使得同样的构建过程可以创建不同的表示.生活中比如说组装电脑,汽车等等这些都是建 ...

  6. PHP设计模式(五)建造者模式(Builder For PHP)

    建造者模式:将一个复杂对象的构造与它的表示分离,使同样的构建过程可以创建不同的表示的设计模式. 设计场景: 有一个用户的UserInfo类,创建这个类,需要创建用户的姓名,年龄,爱好等信息,才能获得用 ...

  7. 每天一个设计模式-7 生成器模式(Builder)

    每天一个设计模式-7 生成器模式(Builder) 一.实际问题 在讨论工厂方法模式的时候,提到了一个导出数据的应用框架,但是并没有涉及到导出数据的具体实现,这次通过生成器模式来简单实现导出成文本,X ...

  8. Stack Overflow 排错翻译 - Closing AlertDialog.Builder in Android -Android环境中关闭AlertDialog.Builder

    Stack Overflow 排错翻译  - Closing AlertDialog.Builder in Android -Android环境中关闭AlertDialog.Builder 转自:ht ...

  9. Joshua Bloch错了? ——适当改变你的Builder模式实现

    注:这一系列都是小品文.它们偏重的并不是如何实现模式,而是一系列在模式实现,使用等众多方面绝对值得思考的问题.如果您仅仅希望知道一个模式该如何实现,那么整个系列都会让您失望.如果您希望更深入地了解各个 ...

随机推荐

  1. Exactly Once 语义

    将服务器的 ACK 级别设置为-1,可以保证 Producer 到 Server 之间不会丢失数据,即 At Least Once 语义. 相对的,将服务器 ACK 级别设置为 0,可以保证生产者每条 ...

  2. Redis分布式锁的正确使用与实现原理

    模拟一个电商里面下单减库存的场景. 1.首先在redis里加入商品库存数量. 2.新建一个Spring Boot项目,在pom里面引入相关的依赖. <dependency> <gro ...

  3. Windows常用的网络命令

    命令 说明 ipconfig 查看网络配置的信息 ping 测试网络是否连通 tracert 网络诊断工具,可以列出分组经过的路由节点 net 有添加用户.开启服务等功能 netstat 显示协议统计 ...

  4. FTP的PORT(主动模式)和PASV(被动模式)

    最近做一个项目用到FTP和其它系统进行文件传输,结果在FTP网络连接的问题上花了很多时间,由于太久没搞多FTP,忘记了FTP不单单开放21端口,客户端采用不同连接模式对网络有不同.在此重温一下FTP的 ...

  5. Markdown进阶

    ### 事项清单 - [x] 拖地 - [x] 擦窗 - [ ] 写作业 - [ ] 交资料 效果 事项清单 [x] 拖地 [x] 擦窗 [ ] 写作业 [ ] 交资料 流程图 graph LR A[ ...

  6. 一文带你读懂!华为云在ACMUG技术沙龙上都透露了些啥?

    摘要:近日,华为云数据库业务总裁苏光牛在ACMUG中国MySQL用户组主办的 "华为云专场" 技术沙龙中分享了华为云数据库重磅新品GaussDB的核心能力与竞争优势.那么, Gau ...

  7. 2020阿里,字节跳动,JAVA岗(一线企业校招、社招)面试题合集

    前言 以下面试题全属于一线大厂社招以及校招的面试真题,各位在做这些题目对照自己的时候请平凡心对待,不要信心受挫.其实 做为致力于一线企业校招或者社招的你来说,能把每个知识模块的一小部分问题去深入学习和 ...

  8. 用过MindManager后才知道思维导图原来这么简单

    哈喽大家好!时间过得真是太快了,一眨眼这一年就接近尾声了,相信我们都度过了不平凡但十分充足的一年,不知道大家在2020年中有没有令自己满意的收获呢? 相信大家各自都有精彩的收获,我们不妨把它们总结一下 ...

  9. 和功能相近的虚拟机软件相比,CrossOver的产品优势有哪些?

    很多用户其实并不喜欢虚拟机软件,他们只是想用回熟悉的Windows应用程序,因为苹果系统与许多软件并不兼容.无奈之下,他们只能安装虚拟机软件.可是虚拟机软件大多比较笨重并且也相对复杂一些,在后期维护上 ...

  10. 解决Jenkins可安装界面是空白的小技巧

    打开后这里面最底下有个[升级站点],把其中的链接改成http的就好了,http://updates.jenkins.io/update-center.json. 然后在服务列表中关闭jenkins,再 ...