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

其实我做的是洛谷的P3943,但是听说fstqwq窃题。。。。。。

题目描述:

小 C 拿来了一长串星型小灯泡,假装是星星,递给小 F,想让小 F 开心一点。不过,有 着强迫症的小 F 发现,这串一共 n 个灯泡的灯泡串上有 k 个灯泡没有被点亮。小 F 决定 和小 C 一起把这个灯泡串全部点亮。

不过,也许是因为过于笨拙,小 F 只能将其中连续一段的灯泡状态给翻转——点亮暗灯 泡,熄灭亮灯泡。经过摸索,小 F 发现他一共能够翻转 m 种长度的灯泡段中灯泡的状态。

小 C 和小 F 最终花了很长很长很长很长很长很长的时间把所有灯泡给全部点亮了。他 们想知道他们是不是蠢了,因此他们找到了你,让你帮忙算算:在最优的情况下,至少需要 几次操作才能把整个灯泡串给点亮?

数据范围:n<=100000,m<=64,k<=8。

思路分析:

我其实是看过  顾z  dalao关于差分的讲评找到这题的(链接就不复了吧,洛谷日报上有的第76期)所以做的时候多少也有点思路,但最后还是看了题解的。

好了,扯了这么多,开始讲题吧!(为了方便理解代码,我们把亮着的灯设为0,没有亮的设为1)

开灯是一种0/1情况,考虑异或差分,那么异或差分有是个什么东西呢?差分大家应该都知道吧,异或差分其实也差不多,注意到对于一个01串,令g2[i]=g1[i]^g1[i+1],那么g1[i]=g2[i]^g2[i-1]^...^g2[1]^g2[0]。这就是异或差分,那么对于一个区间 l-r 取反操作,我们只需要对g2[l]和g2[r+1]取反即可,同时注意g2数组是从0开始的。(g1为原数组,g2为差分数组)

观察g2数组我们发现:由于g1[0]=0,所以g1数组中由0变1,在g2数组中必然是1,而当g1数组中由1变0,在g2数组中也是1,g1数组中有连续的1,那么在g2数组中就是两端为1,中间为0,比如g1数组中有一个1,那么在g2数组就是11,有连续两个1,那么在g2数组就是101,有5个1,那么在g2数组就是100001。

发现:g2数组中最靠近的两个1,设第1个1在位置l,第二个1在位置r,那么对应的在g1数组中便是有连续r-l个1。

既然我们要对g1数组进行区间取反操作,那么在g2数组中的体现就是l和r+1取反(这个之前说了)。假如我们要取反的区间,在g2数组中一端为0,另一端为1,取反之后便是一端为1,另一端为0,也就相当于是换了个位置,也可以看做1在移动,对吧。那么假如是两端都为1,那么取反之后就都变为0,灯就灭了,不是吗?我们就可以看做两个1移到了一起。那么假如是两个0呢?这肯定不划算啊!

既然这样的话,问题就转化为了序列上有(2~2k)个1,有m种不同的移动距离,你需要通过这m这移动方式,让这些1每个都和一个另外的1走到一起,求出最小移动次数。

然后问题就很简单了,最短路+状压直接上。(这里因为边权都是1所以可以用BFS写更快)

同时在注意一下要用类愤怒的小鸟的优化,优化掉一个k。时间复杂度是O(2k*m+2k*22k

代码实现:

type
  hehe=record
    x,dist:longint;
end;
var
  a,b,c,f,dp,v,d:array[-200000..200000]of longint;
  dist:array[1..16,0..100000]of longint;
  q:array[1..1000000]of hehe;
  cha:array[1..16,1..16]of longint;
  i,j,sta,n,m,tot,k,oo:longint;
function min(a,b:longint):longint;
begin
  if a<b then exit(a) else exit(b);
end;
function lowunbit(x:longint):longint;
var
  i:longint;
begin
  i:=0;
  while x>0 do
  begin
    inc(i);
    if x mod 2=0 then exit(i);
    x:=x div 2;
  end;
  exit(i+1);
end;
procedure bfs(start:longint);
var
  head,tail,i,x,cost:longint;
begin
  dist[start,d[start]]:=0;
  head:=0; tail:=1; q[1].x:=d[start]; q[1].dist:=0;
  while head<>tail do
  begin
    inc(head);
    x:=q[head].x; cost:=q[head].dist;
    for i:=1 to m do
    if (x+v[i]>=0)and(x+v[i]<=n)and(dist[start,x+v[i]]>cost+1) then
    begin
      inc(tail);
      q[tail].x:=x+v[i];
      q[tail].dist:=cost+1;
      dist[start,x+v[i]]:=cost+1;
    end;
  end;
end;
begin
  read(n,k,m);
  for i:=1 to k do
  begin
    read(b[i]);
    a[b[i]]:=1;
  end;
  for i:=1 to m do
    read(v[i]);
  m:=2*m;
  for i:=m div 2+1 to m do
    v[i]:=-v[i-m div 2];
  for i:=0 to n do
  begin
    c[i]:=a[i+1] xor a[i];
    if c[i]=1 then begin inc(tot); d[tot]:=i; end;
  end;
  fillchar(dist,sizeof(dist),$7f);
  for i:=1 to tot do bfs(i);
  for i:=1 to tot do
    for j:=1 to tot do
      cha[i,j]:=(1<<(i-1))or(1<<(j-1));
  fillchar(dp,sizeof(dp),$7f);
  dp[0]:=0;  oo:=dp[1];
  for sta:=0 to 1<<tot-1 do
  if dp[sta]<>oo then
  begin
    i:=lowunbit(sta);
    for j:=i+1 to tot do
    if (i<>j)and((sta>>(j-1)and 1)=0) then
      dp[sta or cha[i,j]]:=min(dp[sta or cha[i,j]],dp[sta]+dist[i,d[j]]);
  end;
  writeln(dp[1<<tot-1]);
end.
posted @ 2018-10-30 10:56  WR_Eternity  阅读(160)  评论(0编辑  收藏  举报