BZOJ3508 开灯 & [校内NOIP2018模拟20181027] 密码锁
Time Limit: 10 Sec Memory Limit: 128 MB
Description
xx作为信息学界的大神,拥有众多的粉丝。为了感谢众粉丝的爱戴,xx决定举办一场晚会。为了气派,xx租了一个巨大的灯屏,这个灯屏有\(m\)行,每行有\(n\)个小灯泡。对于每一行灯,有L种操作方法,第i种表示你能将任意长度恰为\(A_i\)的连续一段灯泡的状态取反(灭变亮,亮变灭)。现对于每一行给定\(K\)个点,要求这K个点发光,其余点必须保持熄灭状态。求每一行达到目标状态的最小操作数。
Input
第一行一个数\(m\),表示LED屏的行数。
对于LED屏的每一行:
第一行为\(n,k,L\),意义见上。
第二行为\(k\)个数,表示要求发光的\(k\)个点。
第三行为\(L\)个数,表示\(L\)种操作方式。
Output
对于LED屏的每一行:如果无法达到目标状态,输出\(-1\),否则输出最少次数。
Sample Input
2
10 8 2
1 2 3 5 6 7 8 9
3 5
3 2 1
1 2
3
Sample Output
2
-1
HINT
对于\(100\%\)的数据,\(T\leq 10\),\(N\leq 10000\),\(K\leq 10\),\(L\leq 100\),\(1\leq A_i\leq N\)。
Source
Solution
一个很神仙的思路。
发现\(N\)非常大,但是\(K\)非常小,显然是状压DP,但是只状压\(K\)又不太好办。
于是我们发现,原来序列里只会有\(2K\)个点是一段\(0\)与一段\(1\)的间隔的点(我们这里取前一段的最后一个点)。然后我们又发现,不断地对一个段序列取反,实际上是让这一段和等长的只有\(1\)的序列异或。而这样之后,取反的区间内,相邻两个点的相对状态不会改变,即相邻两个点是否相等是不会改变的。
因此,我们对原序列\(a_i\)做一个这样的处理,维护这个点与后一个点的异或查分:
\]
这样的话,我们对\(a\)里面连续的一段(\(l..r\))取反,只会改变\(b\)里面的\(b[l-1]\)与\(b[r]\)两个点。
同时,\(a\)数组与\(b\)数组之间的又是唯一确定的关系。所以我们要\(A\)的末状态,等价于对应的\(B\)。
然后我们发现,如果把全\(0\)作为初状态,发光后的作为末状态,这样末状态太乱了,不方便转移。倒不如,倒过来,发光后的为初,全\(0\)为末。然后\(A\)全\(0\),对应的\(B\)也是全\(0\)的。
我们发现,我们实际上只是需要把初始的\(B\)里面的所有\(1\)全部去掉即可。然后,如果两个点坐标差恰好为一个操作时,就可以操作一次,那就是把这两点取反,中间的点不变!。那么我们要算出只取反\(i\)和\(j\) 需要的操作次数\(f[i][j]\),其实只需要从\(i\)出发跑BFS最短路即可。
然后考虑如何求出总的操作次数。
我们发现,\(B\)数列中最多有\(2K\) 个点为\(1\),所以我们只应该把那\(2K\)个点取反,其他点都不能动。那么我们状压一下这些点。然后就是一个非常显而易见的DP。\(dp[S]\)表示\(S\)里面的点已经完成了取反的任务。
\]
然后我们类似于愤怒的小鸟的优化,这里会产生很多重复的转移,我们的\(i\)只需要取\(S\)中的最小的点就可以了。
最后答案为\(dp[full\_set]\)。
时间复杂度\(O(nmk+2^kk)\)
#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<cstdlib>
#define inf 1000000000
#define N 10005
#define M 2000005
#define T 45
using namespace std;
inline int read()
{
int x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x*=10;x+=ch-'0';ch=getchar();}
return x*f;
}
int n,K,m,cnt;
int x[N],size[N],a[N],num[N];
bool vis[N],mark[M];
int dis[N],d[25][25];
int q[N];
int f[M];
void bfs(int x)
{
memset(vis,0,sizeof(vis));
int t=0,w=1;
dis[x]=0;q[t]=x;vis[x]=1;
while(t!=w)
{
int now=q[t];t++;
for(int i=1;i<=m;i++)
{
if(now+size[i]<=n&&(!vis[now+size[i]]))
{
vis[now+size[i]]=1;
dis[now+size[i]]=dis[now]+1;
q[w++]=now+size[i];
}
if(now-size[i]>0&&(!vis[now-size[i]]))
{
vis[now-size[i]]=1;
dis[now-size[i]]=dis[now]+1;
q[w++]=now-size[i];
}
}
}
for(int i=1;i<=n;i++)
if(num[i])
{
if(!vis[i])d[num[x]][num[i]]=inf;
else d[num[x]][num[i]]=dis[i];
}
}
int dp(int x)
{
if(!x)return 0;
if(mark[x])return f[x];
mark[x]=1;
f[x]=inf;
int st=0;
for(int i=1;i<=cnt;i++)
{
if(x&(1<<(i-1)))
{
if(!st)st=i;
else
{
if(d[st][i]!=inf)
f[x]=min(f[x],dp(x^(1<<(st-1))^(1<<(i-1)))+d[st][i]);
}
}
}
return f[x];
}
int main()
{
freopen("password.in","r",stdin);
freopen("password.out","w",stdout);
n=read();K=read();m=read();
for(int i=1;i<=K;i++)
{
x[i]=read();
a[x[i]]=1;
}
for(int i=1;i<=m;i++)size[i]=read();
for(int i=n+1;i;i--)a[i]^=a[i-1];
n++;
for(int i=1;i<=n;i++)
if(a[i])
num[i]=++cnt;
for(int i=1;i<=n;i++)
if(a[i])bfs(i);
dp((1<<cnt)-1);
if(f[(1<<cnt)-1]==inf)printf("-1");
else printf("%d",f[(1<<cnt)-1]);
return 0;
}
BZOJ3508 开灯 & [校内NOIP2018模拟20181027] 密码锁的更多相关文章
- NYOJ 题目77 开灯问题(简单模拟)
开灯问题 时间限制:3000 ms | 内存限制:65535 KB 难度:1 描述 有n盏灯,编号为1~n,第1个人把所有灯打开,第2个人按下所有编号为2 ...
- BZOJ2143 飞飞侠 & [校内NOIP2018模拟20181026] 最强大脑
Time Limit: 50 Sec Memory Limit: 259 MB Description 飞飞国是一个传说中的国度,国家的居民叫做飞飞侠.飞飞国是一个N×M的矩形方阵,每个格子代表一个街 ...
- bzoj3508: 开灯
题目链接 题解 设\(b[i]=a[i]\ xor\ a[i+1]\) 我们可以发现,修改只会改变\(b[l-1]\)和\(b[r]\) 然后发现\(b[i]=1\)的点最多\(2*k\)个 状压\( ...
- 【BZOJ3508】开灯
[BZOJ3508]开灯 题面 bzoj 题解 其实变为目标操作和从目标操作变回来没有区别,我们考虑从目标操作变回来. 区间整体翻转(\(\text{Xor}\;1\))有点难受,我们考虑将这个操作放 ...
- c语言实现开灯问题
开灯问题: 有n盏灯,编号为1~n,第1个人把所有灯打开,第2个人按下所有编号为2 的倍数的开关(这些灯将被关掉),第3 个人按下所有编号为3的倍数的开关(其中关掉的灯将被打开,开着的灯将被关闭),依 ...
- Jquery开灯关灯效果
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/ ...
- 9509 开灯(dfs)
9509 开灯 时间限制:1000MS 内存限制:65535K提交次数:0 通过次数:0 题型: 编程题 语言: G++;GCC Description 有16的开关分别控制16盏灯,开关排列成 ...
- 洛谷 P1876 开灯(思维,枚举,规律题)
P1876 开灯 题目背景 该题的题目是不是感到很眼熟呢? 事实上,如果你懂的方法,该题的代码简直不能再短. 但是如果你不懂得呢?那...(自己去想) 题目描述 首先所有的灯都是关的(注意是关!),编 ...
- 【Luogu1876】开灯(数论)
[Luogu1876]开灯(数论) 题面 题目描述 首先所有的灯都是关的(注意是关!),编号为1的人走过来,把是一的倍数的灯全部打开,编号为二的的把是二的倍数的灯全部关上,编号为3的人又把是三的倍数的 ...
随机推荐
- WEB前端开发的思考与感悟
当我想要认真写一篇文章向大家分享我对前端的认识与感悟的时候,突然就深刻的体会到了这句话确实太有道理了. 最近几年对于web前端的传闻很多,比如人才稀缺,简单易学,待遇丰厚,整体势头发展良好等等.遇到过 ...
- js点击获取—通过JS获取图片的相对坐标位置
一.通过JS获取鼠标点击时图片的相对坐标位置 源代码如下所示: <!DOCTYPE html> <html lang="en"> <head> ...
- content is not supported outside 'script" or asp content' region
https://stackoverflow.com/questions/48915080/asp-net-content-is-not-supported-outside-the-script-or- ...
- Java多线程,实现卖电影票的业务
本篇重点:多线程共享资源时发生的互斥问题 一般的我们售卖电影票或者火车票时会有多个窗口同时买票, 我们来看测试代码:主方法new一个Ticket(一个堆),之后三个线程来启动(三个窗口买票) clas ...
- EZOJ #373排序
分析 它居然真的是个nlog^3暴力?! 两个数在加小于min(lowbit(x),lowbit(y))的数时对他们的奇偶性不影响 因此每次加上min(lowbit(x),lowbit(y))判断此时 ...
- 部署 H3C CAS E0306
目录 目录 前文列表 H3C CAS CVK Cloud Virtualization Kernel 虚拟化内核平台 CVMCloud Virtualization Manager 虚拟化管理系统 C ...
- vue-slot的使用
父组件在子组件内套的内容,是不显示的:vue有一套内容分发的的API,<slot>作为内容分发的出口,假如父组件需要在子组件内放一些DOM,那么这些DOM是显示.不显示.在哪个地方显示.如 ...
- mooc-IDEA 应用快捷键自动创建测试类--010
十六.IntelliJ IDEA -应用快捷键自动创建测试类 Step1:在类或接口上,按ctrl+shift+t 选择Create New Test... 则在相应测试包下.创建该测试类. 测试类:
- SQL查询返回去除重复数据的结果集
方法一: select * from tablename where id in (select id from tablename group by id havin ...
- "CoolShell puzzle game" writeup
地址:http://fun.coolshell.cn/ Fuck your brain 看到一大串符号,还以为是 js 代码,结果放到 Chrome 控制台执行没有任何结果,然后搜了一下发现有一门叫B ...