题目链接


\(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. kali 局域网嗅探

    1.局域网图片嗅探 工具  arpspoof arpspoof -i eth0 -t 192.1681.10(网卡 目标地址) 192.168.1.1 局域网网关,如果在Windows中可以使用局域网 ...

  2. Mesh无线网络的定义与WiFi的区别

    Mesh无线网络的定义与WiFi的区别 无线Mesh网络(无线网状网络)也称为「多跳(multi-hop)」网络,它是一种与传统无线网络完全不同的新型无线网络技术.无线网状网是一种基于多跳路由,对等网 ...

  3. cf1108E2 线段树类似扫描线

    /* 有点像扫描线 思路:从左到右枚举每个点,枚举到点i时,把所有以i为起点的区间的影响删去 再加上以i-1为结尾的区间的影响 */ #include<bits/stdc++.h> usi ...

  4. ubuntu下配置反向代理

    1. 环境 ubuntu:Ubuntu 13.04 x86-64 apache2: 2.2.22-6ubuntu5.1 amd64 2. 配置 2.1  配置应用 增加监听端口 打开/etc/apac ...

  5. Java 骚操作--生成二维码

    https://www.cnblogs.com/lsy131479/p/8808172.html

  6. Git和Github入门教程

    一.常用命令 所有命令前都要加 git,如表中的init是指 git init.点击命令可直接跳转至本文第一次使用的地方.以下命令都在命令行里执行. 1.本地命令 行为 命令 备注 初始化 init ...

  7. codeforces div2 C题思路训练【C题好难,我好菜】

    1017C The Phone Number: 构造数列使得LIS和LDS的和最小,定理已知LIS=L,LDS=n/L的向上取整,根据样例可以得到设置L=根号n,构造方法如样例 截断法构造,不用考虑边 ...

  8. linux改权限

    改变文件夹本身权限,不改动子文件(夹) chmod 600 my/ 改变文件夹及子目录下所有文件(夹)权限 chmod -R 777 my/ 统一修改 cd my 修改文件夹权限为755 find - ...

  9. bat处理快速安装jdk脚本

  10. Flink-- 数据输出Data Sinks

    flink在批处理中常见的sink 1.基于本地集合的sink(Collection-based-sink) 2.基于文件的sink(File-based-sink) 基于本地集合的sink(Coll ...