题目链接


\(Description\)

给定一个长为\(n\)的足迹序列(只包含\(L,R\)两种字符),你需要\(LRLRLR...\)这样交替在\(L\)和\(R\)上走(第一步可以选择\(L\)也可以选\(R\))。当你在\(L\)时,下一步可以走到任意一个没走过的\(R\);在\(R\)时,下一步可以走到任意一个没走过的\(L\)。求走完这个\(L,R\)序列最少需要往回走几次,并输出方案(往回走是指从位置\(i\)走到位置\(j\),\(j\lt i\))。保证存在一组可行方案。

\(n\leq10^5\)。

\(Solution\)

首先\(L,R\)数量差大于\(1\)无解。

如果我们需要往回走\(k\)次,那么我们可以将序列分成\(k+1\)个分别合法的子序列。

反过来,如果我们能将序列分成\(k\)个合法的子序列,小于往回走的次数是不是一定小于\(k\)?

考虑能否证明。我们将序列分成\(LL,RR,LR,RL\)四种合法的子序列(第一个字符表示序列开始是什么,第二个字符表示序列末尾是什么,因为只需要关心首尾字符),假设四种子序列分别有\(a,b,c,d\)个。首先有\(|a-b|\leq1\)。

然后我们可以用不超过\(a+b-1\)步将所有\(LL,RR\)合并成一个序列,这个序列可能是四种中的任意一种。

还可以用不超过\(c-1\)步将所有\(LR\)合并成一个\(LR\)。对\(RL\)同理。这样我们就得到了一个\(LR\)和一个\(RL\)子序列。

如果把其中一个子序列的最后一个字符给另一个,就可以变成一个\(LL\)和一个\(RR\),且不会增加次数。

那么不论刚开始\(LL,RR\)合并出的子序列是哪种,都可以用不超过两步拼接上剩下的两个子序列。

这样最多使用\(a+b-1+c-1+d-1+2=k-1\)步。这样就证明了,如果我们能能将序列划分成\(k\)个合法子序列,一定可以构造方案使得最多往回走\(k-1\)次。(可是我还是觉得有点迷...)

现在的问题是怎么讲序列划分成尽量少的合法子序列。

我们发现可以跑最小路径覆盖(每个\(L/R\)向它后面所有\(R/L\)连边,\(n-最大匹配数\)就是答案)(图在官方题解里有)。

但还可以发现这个匹配实际上可以直接贪心,每个字符选它能匹配的点中最靠前的匹配即可。因为一定可以调整最大匹配使得满足这种贪心。

那么复杂度就是\(O(n)\)的了。

具体做的时候,可以拿个栈模拟一下拼接...(菜到不会写.jpg)


//46ms	1900KB
#include <queue>
#include <cstdio>
#include <cstring>
#include <algorithm>
#define AE(u,v) nxt[u]=v, pre[v]=u
typedef long long LL;
const int N=1e5+5; int top,pre[N],nxt[N];
std::queue<int> q[2];
char s[N];
struct Node
{
int l,r;
}sk[N]; bool Maintain()
{
if(top<=1) return 0;
Node a=sk[top],b=sk[top-1];
if(s[a.r]!=s[b.l])
{
AE(a.r,b.l), sk[--top]=(Node){a.l,b.r};
return 1;
}
if(s[a.l]!=s[b.r])
{
AE(b.r,a.l), sk[--top]=(Node){b.l,a.r};
return 1;
}
if(s[a.l]!=s[b.l])//LR RL
{
if(a.r>b.r) std::swap(a,b);
int p=pre[b.r];
AE(a.r,b.r), AE(b.r,b.l), nxt[p]=0, sk[--top]=(Node){a.l,p};
return 1;
}
return 0;
} int main()
{
scanf("%s",s+1);
const int n=strlen(s+1);
for(int i=1,c; i<=n; q[c^1].push(i++))
if(!q[c=s[i]=='L'].empty()) AE(q[c].front(),i), q[c].pop();
int ans=0;
for(int i=1; i<=n; ++i)
if(!pre[i])
{
++ans;
int p=i; while(nxt[p]) p=nxt[p];
sk[++top]=(Node){i,p};
while(Maintain());
}
printf("%d\n",ans-1);
for(int x=sk[1].l; x; x=nxt[x]) printf("%d ",x); return 0;
}

Codeforces.578E.Walking(构造)的更多相关文章

  1. CodeForces 578E Walking!

    题意 略. 题解 好毒瘤啊,我最多就口胡第一问的样子吧. 第一问很显然(跟凤凰县探险队员一样显然),就是每次贪心选长度最大的满足条件的子序列,选不到就折返回来.所以折返的次数很明显就是选出子序列的个数 ...

  2. codeforces 1041 e 构造

    Codeforces 1041 E 构造题. 给出一种操作,对于一棵树,去掉它的一条边.那么这颗树被分成两个部分,两个部分的分别的最大值就是这次操作的答案. 现在给出一棵树所有操作的结果,问能不能构造 ...

  3. Codeforces - 474D - Flowers - 构造 - 简单dp

    https://codeforces.com/problemset/problem/474/D 这道题挺好的,思路是这样. 我们要找一个01串,其中0的段要被划分为若干个连续k的0. 我们设想一个长度 ...

  4. Codeforces Global Round 8 B. Codeforces Subsequences(构造)

    题目链接:https://codeforces.com/contest/1368/problem/B 题意 构造最短的至少含有 $k$ 个 $codeforces$ 子序列的字符串. 题解 如下表: ...

  5. Codeforces 410C.Team[构造]

    C. Team time limit per test 1 second memory limit per test 256 megabytes input standard input output ...

  6. Codeforces 716C[数论][构造]

    /* CF傻逼构造题 某人要经过n回合游戏,初始分值是2,等级为1. 每次有两种操作 1.无条件,分值加上自己的等级数. 2.当目前的数字是完全平方数并且该数字开方以后是等级数加1的整数倍,那么可以将 ...

  7. Codeforces 1154D - Walking Robot - [贪心]

    题目链接:https://codeforces.com/contest/1154/problem/D 题解: 贪心思路,没有太阳的时候,优先用可充电电池走,万不得已才用普通电池走.有太阳的时候,如果可 ...

  8. Tea Party CodeForces - 808C (构造+贪心)

    Polycarp invited all his friends to the tea party to celebrate the holiday. He has ncups, one for ea ...

  9. New Roads CodeForces - 746G (树,构造)

    大意:构造n结点树, 高度$i$的结点有$a_i$个, 且叶子有k个. 先确定主链, 然后贪心放其余节点. #include <iostream> #include <algorit ...

随机推荐

  1. hdu2460 e-DCC染色缩点+暴力LCA

    /* 给定一个无向图,往里面加边,问加第i条边时图中的桥数 首先肯定要求初始状态下的桥,染色缩点 每次给定的边为(u,v), 那么u->lca(u,v)->v路上的所有边都不再是桥 求LC ...

  2. Nginx详解三:Nginx基础篇之yum安装

    Nginx快速搭建 Mainline version ----开发版:具有最新功能的版本,用于测试.研究.学习,不用于企业生成环境 Stable version----稳定版:官方认可,且通过测试的 ...

  3. golang 打包,交叉编译,压缩

    打包,压缩 我们的常规打包方式 $ go build Mac下我们用 ls -lh查看,可以看到我们打包出来的可执行文件会比较大,一般只写几行代码就回又3M以上的文件大小了. 我们的带压缩的打包方式 ...

  4. Parameter 'name' not found. Available parameters are [arg1, arg0, param1, param2]

    解决方法: <select id="selectIf" resultType="student"> SELECT id,name,age,score ...

  5. python字符串之split

    函数:split() Python中有split()和os.path.split()两个函数,具体作用如下:split():拆分字符串.通过指定分隔符对字符串进行切片,并返回分割后的字符串列表(lis ...

  6. C/C++遍历二维数组,列优先(column-major)比行优先(row-major)慢,why?

    C/C++遍历二维数组,列优先(column-major)比行优先(row-major)慢,why? 简单粗暴的答案:存在Cache机制! 稍微啰嗦一点:CPU访问内存(读/写,遍历数组的话主要是读) ...

  7. python--使用队列结构来模拟共享打印机等候时间

    按书里的样例抄的. 可以看到,将打印速度由第分钟5页提高到10页之后, 每个学生提交打印任务到打印完成的时间明显缩短. =========================== 在计算机科学实验室里考虑 ...

  8. javascript 语句和严格模式(三)

    一.语句 javascript程序由语句组成,语句遵守特定的语法规则. block break continue empty if...else switch try catch var functi ...

  9. 清北合肥day2-day5

    day2:215这一天的题目相对比较模板化t1:50看错了数据范围求n个点到给出的点哈夫曼距离的最小值我想到的是一种非常zz的做法我们二分答案,然后判断是否在这个距离内有点但是这样前缀和不是很好维护于 ...

  10. Vue小问题汇总

    1.element-UI等组件更改默认样式: >>> https://vue-loader-v14.vuejs.org/zh-cn/features/scoped-css.html ...