Description

有一个 01 序列 \(a_1,a_2,\cdots,a_n\),初始时全为 \(0\)。

给定 \(m\) 个长度,分别为 \(l_1\sim l_m\)。

每次可以选择一个长度为某个 \(l_i\) 区间,对其进行翻转操作(\(0\to 1,1\to 0\))。

求最少的操作次数,使得最后有且仅有 \(k\) 个位置为 \(1\)(\(k\) 个位置给定),其余为 \(0\)。

\(1\leq n\leq 10^4,1\leq k\leq 10,1\leq m\leq 100\)。

Solution

原问题等价于:

给定 01 序列 \(a_1,a_2,\cdots,a_n\),有 \(k\) 个位置为 \(1\),其余为 \(0\)。每次可以翻转长度为 \(l_i\) 的区间,求将 \(a\) 清零的最小操作数。

操作为区间修改,考虑差分。

由于是“区间取反”,一般的作差差分无法使用,考虑 异或差分。令 \(b_i=a_i\text{ xor }a_{i+1}\)(设 \(a_0=a_{n+1}=0\))。

那么,将原序列中的区间 \([l,r]\) 翻转,等价于将差分序列中的 \(b_{l-1},b_r\) 取反(其他元素不变)。

Step1

考虑到 \(b\) 序列初始最多只有 \(2k\) 个 \(1\),则问题转化为:

给定 01 序列 \(b_0,b_2,\cdots,b_n\),最多有 \(2k\) 个位置为 \(1\)。每次可以选择一对距离为 \(l_i\) 的位置,将其取反。求将 \(b\) 清零的最小操作次数。

设选择的一对位置为 \((x,y)\)。分类讨论:

  • 若 \(b_x=0,b_y=0\),则操作后 \(b_x=1,b_y=1\),增加 \(2\) 个 \(1\)。(显然会使答案更劣,不会发生)

  • 若 \(b_x=1,b_y=1\),则操作后 \(b_x=0,b_y=0\),相当于 \(2\) 个 \(1\) 碰撞变成 \(0\),减少 \(2\) 个 \(1\)。

  • 若 \(b_x=1,b_y=0\),则操作后 \(b_x=0,b_y=1\),相当于把 \(x\) 上的 \(1\) 移到 \(y\),\(1\) 的数量不变。

  • 若 \(b_x=0,b_y=1\),与 \(b_x=1,b_y=0\) 同理,\(1\) 的数量不变。

Step2

问题等价于:(第 \(i\) 个节点有标记相当于 \(b_i=1\))

给定一个有 \(n+1\) 个节点的图(点的编号为 \(0\sim n\))。当 \(dis(x,y)=l_i\) 时,存在边 \((x,y)\)。初始时最多有 \(2k\) 个节点上有标记,每次可以沿边移动标记。两个标记相遇就会消失。求使所有标记消失的最少移动次数。

设标记点分别为 \(p_0,p_1,\cdots,p_{g-1}\)。

首先,我们可以通过 BFS 计算出所有标记点对之间的距离。

\(2k\leq 20\),考虑 状压 DP(差分序列中为 \(0\) 的位置不用管,只考虑 \(2k\) 个 \(1\),有 \(2^{2k}\) 种状态)。令 \(f_S\) 表示标记点状态为 \(S\) 时使所有标记消失的最少移动次数。

(\(S\) 二进制下的第 \(i\) 位为 \(1\) 表示标记点 \(p_i\) 上的标记未消失。显然转移只需考虑 \(2\) 个 \(1\) 碰撞变成 \(0\) 的情况,其他情况都是没有意义的,所以我们不需要考虑非初始标记点的状态)

转移:设 \(S\) 二进制下为 \(1\) 的其中两个位为 \(i,j\),\(f_{S}=\min\{f_{S-2^i-2^j}+dis(i,j)\}\)。

初始时 \(f_0=0,f_i=\infty\,(i\neq 0)\)。答案即为 \(f_{2^g-1}\)。

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=1e4+5,M=22;
int n,m,k,x,a[N],b[N],l[N],g,c[N],d[N],dis[M][M],f[1<<M],ans;
bool v[N];
queue<int>q;
void bfs(int s){ //BFS 计算出所有标记点对之间的距离
for(int i=0;i<=n;i++) d[i]=1e18,v[i]=0;
d[s]=0,v[s]=1,q.push(s);
while(q.size()){
int x=q.front(),y;q.pop();
for(int i=1;i<=m;i++){
if((y=x+l[i])<=n&&!v[y]) d[y]=d[x]+1,v[y]=1,q.push(y);
if((y=x-l[i])>=0&&!v[y]) d[y]=d[x]+1,v[y]=1,q.push(y);
}
}
for(int i=0;i<=n;i++)
if(b[i]) dis[c[s]][c[i]]=d[i];
}
signed main(){
scanf("%lld%lld%lld",&n,&k,&m);
for(int i=1;i<=k;i++)
scanf("%lld",&x),a[x]=1;
for(int i=1;i<=m;i++)
scanf("%lld",&l[i]);
for(int i=0;i<=n;i++)
b[i]=a[i]^a[i+1],c[i]=(b[i]?g++:0); //b 为差分序列。若节点 i 是标号点,也就是 b[i]=1,则节点 i 对应的标记点编号为 c[i](编号从 0 开始)
for(int i=0;i<=n;i++)
if(b[i]) bfs(i); //注意这里是 if(b[i]) 而不是 if(c[i]),因为标记点的编号是从 0 开始的
for(int s=1;s<(1<<g);s++){ //状压 DP
f[s]=1e18;
for(int i=0;i<g;i++){
if(!((s>>i)&1)) continue;
for(int j=i+1;j<g;j++) //枚举 S 二进制下为 1 的两个位为 i,j
if((s>>j)&1) f[s]=min(f[s],f[s-(1<<i)-(1<<j)]+dis[i][j]);
}
}
ans=f[(1<<g)-1],printf("%lld\n",ans==1e18?-1:ans);
return 0;
}

「Codeforces 79D」Password的更多相关文章

  1. 「CodeForces 581D」Three Logos

    BUPT 2017 Summer Training (for 16) #3A 题意 给你三个矩形,需要不重叠不留空地组成一个正方形.不存在输出-1,否则输出边长和这个正方形(A,B,C表示三个不同矩形 ...

  2. 「CodeForces - 50C 」Happy Farm 5 (几何)

    BUPT 2017 summer training (16) #2B 题意 有一些二维直角坐标系上的整数坐标的点,找出严格包含这些点的只能八个方向走出来步数最少的路径,输出最少步数. 题解 这题要求严 ...

  3. 「CodeForces - 598B」Queries on a String

    BUPT 2017 summer training (for 16) #1I 题意 字符串s(1 ≤ |s| ≤ 10 000),有m(1 ≤ m ≤ 300)次操作,每次给l,r,k,代表将r位置插 ...

  4. 「CodeForces - 717E」Paint it really, really dark gray (dfs)

    BUPT 2017 summer training (for 16) #1H 题意 每个节点是黑色or白色,经过一个节点就会改变它的颜色,一开始在1节点.求一条路径使得所有点变成黑色. 题解 dfs时 ...

  5. 「CodeForces 476A」Dreamoon and Stairs

    Dreamoon and Stairs 题意翻译 题面 DM小朋友想要上一个有 \(n\) 级台阶的楼梯.他每一步可以上 \(1\) 或 \(2\) 级台阶.假设他走上这个台阶一共用了 \(x\) 步 ...

  6. 「CodeForces 546B」Soldier and Badges 解题报告

    CF546B Soldier and Badges 题意翻译 给 n 个数,每次操作可以将一个数 +1,要使这 n 个数都不相同, 求最少要加多少? \(1 \le n \le 3000\) 感谢@凉 ...

  7. CodeForces 79D 【Password】,洛谷P3943 【星空】

    其实我做的是洛谷的P3943,但是听说fstqwq窃题...... 题目描述: 小 C 拿来了一长串星型小灯泡,假装是星星,递给小 F,想让小 F 开心一点.不过,有 着强迫症的小 F 发现,这串一共 ...

  8. 「Codeforces 468C」Hack it!

    Description 定义 \(f(x)\) 表示 \(x\) 的各个数位之和.现在要求 \(\sum_{i=l}^rf(i)\bmod a\). 显然 ans=solve(l,r)%a; if(a ...

  9. 「Codeforces 724F」Uniformly Branched Trees

    题目大意 如果两棵树可以通过重标号后变为完全相同,那么它们就是同构的. 将中间节点定义为度数大于 \(1\) 的节点.计算由 \(n\) 个节点,其中所有的中间节点度数都为 \(d\) 的互不同构的树 ...

随机推荐

  1. 在Idea上用JDBC连接mysql数据库

    一.前言 本次操作建立在idea中java环境已配置的基础上 二.操作步骤 1.建立Web项目后,添加驱动包 mysql-connector-java-5.0.8-bin.jar (1)下载mysql ...

  2. 学习java 7.9

    学习内容: Date类 Date类常用方法 SimpleDateFormat 1.格式化(从Date到String) public final String format(Date date) 将日期 ...

  3. Vue2全家桶+Element搭建的PC端在线音乐网站

    目录 1,前言 2,已有功能 3,使用 4,目录结构 5,页面效果 登录页 首页 排行榜 歌单列表 歌单详情 歌手列表 歌手详情 MV列表 MV详情 搜索页 播放器 1,前言 项目基于Vue2全家桶及 ...

  4. Flume(二)【入门】

    目录 一.安装部署 1.安装地址 2.安装步骤 二.入门案例 1.官方案例(nestat->logger) 2.实时监控单个追加文件(exec->hdfs) 3.实时监控目录下多个新文件( ...

  5. Linux启动初始化配置文件

    Linux启动初始化配置文件(1)/etc/profile 登录时,会执行. 全局(公有)配置,不管是哪个用户,登录时都会读取该文件. (2)/ect/bashrc Ubuntu没有此文件,与之对应的 ...

  6. Mysql的行级锁

    我们首先需要知道的一个大前提是:mysql的锁是由具体的存储引擎实现的.所以像Mysql的默认引擎MyISAM和第三方插件引擎 InnoDB的锁实现机制是有区别的. Mysql有三种级别的锁定:表级锁 ...

  7. spring-dm 一个简单的实例

    spring-dm2.0  运行环境,支持JSP页面 运行spring web 项目需要引用包

  8. 【VSCode】检测到 #include 错误。请更新 includePath。已为此翻译单元(C:\mingw-w64\i686-8.1.0-posix-dwarf-rt_v6-rev0\mingw32\i686-

    win+r 运行cmd 输入"gcc -v -E -x c -"获取mingw路径: 我的: #include "..." search starts here ...

  9. 【JAVA今法修真】 第三章 关系非关系 redis法器

    您好,我是南橘,万法仙门的掌门,刚刚从九州世界穿越到地球,因为时空乱流的影响导致我的法力全失,现在不得不通过这个平台向广大修真天才们借去力量.你们的每一个点赞,每一个关注都是让我回到九州世界的助力,兄 ...

  10. Jenkins性能测试

    目录 一.简介 二.JMeter测试 一.简介 Taurus是-个开源的自动化框架,用于运行各种开源负载测试工具和功能测试工具.其支持最流行的开源负载测试工具Apache JMeter.Seleniu ...