一道很好的锻炼思维难度的题,如果您能在考场上直接想出来的话,提高组450分以上就没问题了吧。(别像作者一样看了好几篇题解才勉强会)

  先提取出题目大意:给定一个长度n<=40000的01串,其中1的个数<=8,有m种操作,每次操作都是把一个该操作对应长度的区间取反,或者说异或上1,求使整个串变为只有0的串的最小操作次数。 

  首先对于一次操作,肯定不能暴力地一个个去取反吧。优化区间操作,要么用数据结构,要么用前缀和或差分。其实这里可以用差分来优化:建立新的下标最大为n+1、下标正常从1开始的d数组,d[i]=a[i]^a[i-1],每次区间修改时,设修改的区间的左端点为l,右端点为r,只要让a[l]和a[r+1]分别异或上1就行了。

    解释一下:这里的差分中的“差”已经不能简单理解为减法做差了,而是逻辑上的“差距”。差分维护的是相邻元素间的逻辑关系,从而使能从初始状态(a[0])通过差分数组表达的逻辑关系推出某个位置上a的值(从形式上看就是求前缀)。对于异或来说,正好满足这样的性质:我们读入串时从a[1]开始读入,那a[0]没管它的话就会是0,那么发现从它开始向后与d数组做前缀异或时,设当前做到第i个位置了(即当前值=a[0]^d[1]^d[2]^……^d[i]),则当前值就是a[i]的值,同样对于差分优化的区间操作来说,对  左端点  和   右端点+1  处取反后,在求一遍前缀异或,发现对于那个要修改的区间,真的就取反了。(可以这么考虑:对于区间中的位置来说,修改后再求完前缀后,每位都比修改前的这位多异或了1,故取反;对于区间后面的位置,修改后再求完前缀后,每位都比修改前的这位多异或了2个1,就不会改变,总体上看,这个区间就被取反了。这要依赖于异或这个运算可交换且有单位元(么元)、且1有对于异或的逆元(其实所有数都有)(逆元可不只限于取模哟))。因为要能实现右端点等于n的区间修改,所以d数组的最大下标为n+1,同时也是为下文1的个数为偶数的结论做铺垫。

  那么问题就变为:给定一个长度n<=40001的01串,其中1的个数<=16,有m种操作,每次操作都是把下标差该操作对应长度的两个数取反,求使整个串变为只有0的串的最小操作次数。

    解释一下:考虑将原串的1全都拿出来后一个个加入到一个全是0的串u里,形成一个与原串完全一样的串,看看u串对应的差分数组的变化,发现每次加入一个1,差分数组要么新增2个1(加入u串的1在u串中左右没有相邻的1),要么有一个1往后或前移一个位置(加入u串的1在u串中的左边或右边中只有一边有相邻的1),要么减少2个1(加入u串的1在u串中的左边和右边都相邻的1),因为要从u串中加最多8个1,显然对应的差分数组最多就16个1,同时差分数组中1的个数一定为偶数。

    显然对于每次操作取反的两个数中一定有一个1,不然这次操作不但没用,还多出了2个1,浪费次数还增多任务。考虑每次取反的两个数:

      1、有1个1:那么结果是原来1的位置现在变成了0,原来0的位置现在变成了1。形象化地,1从原来的位置移动到了另一个位置

      2、有2个1:那么这两个1都会变为0,形象化地一个1移动到了有1的位置,两个1碰到一起就消失了。

  于是问题又被形象化地转化为:给定一个长度n<=40001的01串,其中1的个数<=16,有m种移动长度,每次移动都是把1个1移动这个移动相应的移动长度,若2个1碰到一起(在同一个位置)就会消失,求使整个串的所有1消失的最小移动次数。

    如果我们知道了这些1两两碰到一起消失的最小移动次数,跑个状压DP就可以喽。而这些1两两碰到一起消失的最小移动次数,正可以对每个1跑一遍BFS求得(把串的每一位看成一个点,向这个点能一次移动到的所有点都连一条长度为1的边),时间复杂度O(kmn),完全可以接受。至于状压DP,推荐写O(k*(2的2*K次方))的DP,如果写O((k的平方)*(2的2*K次方))的DP,虽然能过这个题,但是很容易到考场上被卡。关于O(k*(2的2*K次方)),我们可以从当前状态向以后状态转移,当前状态第一个没有被消去的1迟早要被消去,不如先消去,转移到包括这个1的状态,容易发现每一个消去所有1的状态,都可以通过这样的策略实现出来,所以这样是正确的。

最后看看代码吧:

 #include<iostream>
#include<cstdio>
#include<cstring>
#include<queue> using namespace std; const int N=,M=,K=; int n,k,m,x,a[N],d[N],caolen[M+],d1[K<<],cntd1,lst[N],nxt[N*M<<],to[N*M<<],cnt;
int wei[N],vis[N]; long long dp[<<K*],dis[K<<][K<<]; char ch; inline int read()
{
x=;
ch=getchar();
while(!isdigit(ch)) ch=getchar();
while(isdigit(ch)) x=(x<<)+(x<<)+(ch^),ch=getchar();
return x;
} inline void addedge(int u,int v)
{
nxt[++cnt]=lst[u];
lst[u]=cnt;
to[cnt]=v;
} struct node{
int w,dlen;
}head; queue<node>q,ling; inline void bfs(int w,int ord)//变量含义:这个1在原串中的位置,这个1在d1(将d中的1又单独存了一下)中的位置
{
memset(vis,,sizeof vis);
q=ling;
vis[w]=;
int cntin=,t;
q.push((node){w,});
while(!q.empty())
{
head=q.front();
q.pop();
for(int e=lst[head.w];e;e=nxt[e])
if(!vis[t=to[e]])
{
vis[t]=;
cntin++;
if(d[t])
dis[ord][wei[t]]=head.dlen+;
if(cntin==n)
return;
q.push((node){t,head.dlen+});
}
}
} inline void init()
{
n=read(),k=read(),m=read();
for(int i=;i<=k;++i)
a[read()]=;
for(int i=;i<=m;++i)
caolen[i]=read();
for(int i=;i<=n+;++i)
{
d[i]=a[i]^a[i-];
if(d[i])
{
d1[++cntd1]=i;
wei[i]=cntd1;
} }
for(int i=;i<=n+;++i)
for(int j=;j<=m;++j)
{
if(i+caolen[j]<=n+)
addedge(i,i+caolen[j]);
if(i-caolen[j]>)
addedge(i,i-caolen[j]);
}
memset(dis,0x3f,sizeof dis);
for(int i=;i<=cntd1;i++)
bfs(d1[i],i);
} int main()
{
init();
int lim=(<<cntd1)-;
memset(dp,0x3f,sizeof dp);
dp[]=;//下标为1的消去状态(下标的二进制第i位为1:d1中第i个1已被消去)
for(int i=;i<=lim;++i)
{
int j=;
while(i&(<<j))
j++;
for(int t=j+;t<cntd1;t++)
{
if(!(i&(<<t)))
dp[i|(<<j)|(<<t)]=min(dp[i|(<<j)|(<<t)],dp[i]+dis[j+][t+]);
}
}
printf("%lld",dp[lim]);
return ;
}

  

  

洛谷P3943 星空——题解的更多相关文章

  1. 洛谷P3943 星空

    洛谷P3943 星空 题目背景 命运偷走如果只留下结果, 时间偷走初衷只留下了苦衷. 你来过,然后你走后,只留下星空. 题目描述 逃不掉的那一天还是来了,小 F 看着夜空发呆. 天上空荡荡的,没有一颗 ...

  2. [洛谷P3943]:星空(DP+最短路)

    题目传送门 题目背景 命运偷走如果只留下结果, 时间偷走初衷只留下了苦衷.你来过,然后你走后,只留下星空. 题目描述 逃不掉的那一天还是来了,小$F$看着夜空发呆.天上空荡荡的,没有一颗星星——大概是 ...

  3. 洛谷 P3943 星空

    题目背景 命运偷走如果只留下结果, 时间偷走初衷只留下了苦衷. 你来过,然后你走后,只留下星空. 题目描述 逃不掉的那一天还是来了,小 F 看着夜空发呆. 天上空荡荡的,没有一颗星星——大概是因为天上 ...

  4. 洛谷P3943星空

    啦啦啦啦——又是五月天的歌,题目传送门 这道题比之前两道真的不是同一级别的,这里我这个蒟蒻也讲不清,不如看下这位大佬的吧,他的写的已经非常清楚了:Z-Y-Y-S,这里我就只放下我的代码,也是按照这位大 ...

  5. 洛谷NOIp热身赛题解

    洛谷NOIp热身赛题解 A 最大差值 简单树状数组,维护区间和.区间平方和,方差按照给的公式算就行了 #include<bits/stdc++.h> #define il inline # ...

  6. 洛谷P2827 蚯蚓 题解

    洛谷P2827 蚯蚓 题解 题目描述 本题中,我们将用符号 ⌊c⌋ 表示对 c 向下取整. 蛐蛐国最近蚯蚓成灾了!隔壁跳蚤国的跳蚤也拿蚯蚓们没办法,蛐蛐国王只好去请神刀手来帮他们消灭蚯蚓. 蛐蛐国里现 ...

  7. 洛谷P1816 忠诚 题解

    洛谷P1816 忠诚 题解 题目描述 老管家是一个聪明能干的人.他为财主工作了整整10年,财主为了让自已账目更加清楚.要求管家每天记k次账,由于管家聪明能干,因而管家总是让财主十分满意.但是由于一些人 ...

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

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

  9. [POI 2008&洛谷P3467]PLA-Postering 题解(单调栈)

    [POI 2008&洛谷P3467]PLA-Postering Description Byteburg市东边的建筑都是以旧结构形式建造的:建筑互相紧挨着,之间没有空间.它们共同形成了一条长长 ...

随机推荐

  1. iter的特殊用法以及偏函数partial

    iter()的特殊用法 常规使用 iter(obj),会返现一个迭代器,如果 obj 不是可迭代对象,则会报错. 特殊用法(哨兵模式) iter(object[, sentinel]) sentine ...

  2. synchronized锁住的是代码还是对象,以及synchronized底层实现原理

    synchronized (this)原理:涉及两条指令:monitorenter,monitorexit:再说同步方法,从同步方法反编译的结果来看,方法的同步并没有通过指令monitorenter和 ...

  3. JS判断页面是否为浏览器当前页

    function currentPage() { var hiddenProperty = 'hidden' in document ? 'hidden' : 'webkitHidden' in do ...

  4. iOS常用数学常量宏

    在实际工作中有些程序不可避免的需要使用数学函数进行计算,比如地图程序的地理坐标到地图坐标的变换.Objective-C做为ANSI C的扩展,使用C标准库头文件<math.h>中定义的数学 ...

  5. 使用MEMCACHED实现缓存

    什么是memcached Memcached是一个自由开源的,高性能,分布式内存对象缓存系统. Memcached是以LiveJournal旗下Danga Interactive公司的Brad Fit ...

  6. inetd - 因特网“超级服务”

    总览 inetd - [ -d ] [ -q 队列长度 ] [ 配置文件名 ] 描述 inetd通常在系统启动时由/etc/rc.local引导.inetd会监听指定internet端口是否有连接要求 ...

  7. Linux下安装升级python

    本文主要是参考帖子,感谢这位博主,我主要是对相关操作进行补充和说明. 本文主要是在linux(centos)下安装Python3.7.1 1.下载安装Python-3.7.1 1) 下载Python- ...

  8. Linux学习--第十二天--服务、ps、top、pstree、kill、&、jobs、fg、vmstat、dmesg、free、uptime、uname、crontab、ls

    服务分类 linux服务分为rpm包默认安装的服务和源码包安装的服务. rpm包默认安装的服务分为独立的服务和基于xinetd服务. 查询已安装的服务 rpm包安装的服务 chkconfig --li ...

  9. DTM/DEM/DSM/DOM/DLG

    一.DTM (Digital Terrain Model) 数字地面模型是利用一个任意坐标系中大量选择的已知x .y .z 的坐标点对连续地面的一个简单的统计表示,或者说,DTM 就是地形表面形态属性 ...

  10. 一、Signalr WebApi客服

    一.搭建环境 (redis服务) 链接测试 二.项目搭建 参考 1.搭建项目(直接项目-不包含MVC以及API) 项目结构 但是需要访问(所以还需要添加控制器Api的模式)选择Api 添加类库一个专门 ...