拯救LongMM (递推公式求解)

拯救L o n g M M ( l a n . p a s / c / c p p )
【题目描述】
LongDD 将军为了平息延续数年战乱,决定释放战俘营中所有的俘虏。然而,
LongDD 将军不打算释放敌军的统帅LongMM——因为这个家伙异常聪明,是个难缠的
对手。所以LongDD 将军决定把LongMM 用链子固定到墙上。链子由n 个环组成,每
个环有可能在墙上,也可能不在墙上。
“LongDD 将军,你为什么把我绑在墙上,不让我获得自由”,LongMM 咆哮道。
“但是,LongMM,你并没有被绑在墙上。我很确定你可以自己把链子解开”,
LongDD 将军回答道,“但是请你在天黑之前解开,否则我会因为你制造噪音把你重
新抓起来。”
请帮助LongMM 吧!链子由n 个环组成,编号为1,2,…,n。我们可以把每个环从
墙上取下来或者从新放回墙上,但是需要遵循如下规则:
- 每一步只能取下或者装上一个环
- 编号为1 的环可以随意取下或装上
- 如果编号为1,…,k-1 的环都取下了,并且编号为k 的环在墙上,我们可
以随意取下或者装上第k+1 个环
- 当所有环都取下来之后,LongMM 可以逃脱了
给定每个环的初始状态,请你编写程序计算LongMM 最少需要多少步才能逃脱。
程序名: lan
【输入格式】
* 第 1 行: 有一个整数n,(1<=n<=1000),表示环的个数
* 第 2 行: 有n 个整数,第i 个整数O_i=0,表示第i 个环在初始的时候为摘下的
状态;如果O_i=1,表示第i 个环初始的时候为装在墙上的状态。
【输入样例】
4
1 0 1 0
【输出格式】
* 第 1 行: 只有一个整数,表示最少需要多少步才能让LongMM 逃脱。
【输出样例】
6
【输出解释】

先递推 

然后 找规律 发现从后往前 ans=2^jn-2^j(n-1)+2^j(n-2)+.....   (j[i]表示从左往右第i个1)

Program lan;
type
   arr=record
         len:longint;
         d:array[1..1000] of longint;
       end;
const
    F=1000000;
var
   n,i:longint;
   ans:arr;
   p2:array[1..1000] of arr;
   a:array[1..1001] of longint;
procedure multipy;
var
   i,j,k:longint;
begin
   for k:=2 to 1000 do
   begin
      p2[k].len:=p2[k-1].len;
      for i:=1 to p2[k-1].len do
      begin
         inc(p2[k].d[i],p2[k-1].d[i]*2);
         inc(p2[k].d[i+1],p2[k].d[i] div F);
         p2[k].d[i]:=p2[k].d[i] mod F;
      end;
      if p2[k].d[p2[k].len+1]<>0 then inc(p2[k].len);



   end;
   for k:=1 to 1000 do dec(p2[k].d[1]);




end;
function max(a,b:longint):longint;
begin
   if a>b then exit(a) else exit(b);
end;
Procedure add(a,b:arr;var c:arr);
var
   i,j:longint;
begin
   fillchar(c,sizeof(c),0);
   c.len:=max(a.len,b.len);
   for i:=1 to c.len do
   begin
      inc(c.d[i],a.d[i]+b.d[i]);
      inc(c.d[i+1],c.d[i] div F);
      c.d[i]:=c.d[i] mod F;
   end;
   if c.d[c.len+1]>0 then inc(c.len);




end;
procedure sub(a,b:arr;var c:arr);
var
   i,j:longint;
begin
   fillchar(c,sizeof(c),0);
   c.len:=max(a.len,b.len);
   for i:=1 to c.len do
   begin
      inc(c.d[i],a.d[i]-b.d[i]);
      if c.d[i]<0 then
      begin
         inc(c.d[i],F);
         dec(c.d[i+1],1);
      end;
   end;
   while (c.len>1) and (c.d[c.len]=0) do dec(c.len);


end;
Procedure work;
var
   i,j:longint;
   flag:boolean;
begin
   i:=n;
   flag:=true;
   while true do
   begin
      while (a[i]=0) and (i>1) do dec(i);
      if a[i]=0 then exit;
      if flag then add(ans,p2[i],ans)
      else sub(ans,p2[i],ans);

      dec(i);
      if i=0 then exit;
      flag:=not(flag);
   end;



end;
Procedure pri;
var
   i:longint;
begin
   write(ans.d[ans.len]);
   for i:=ans.len-1 downto 1 do
   begin
      if ans.d[i]<100000 then write('0');
      if ans.d[i]<10000 then write('0');
      if ans.d[i]<1000 then write('0');
      if ans.d[i]<100 then write('0');
      if ans.d[i]<10 then write('0');
      write(ans.d[i]);
   end;
   writeln;
end;

begin
   assign(input,'lan.in');
   assign(output,'lan.out');
   reset(input);
   rewrite(output);
   read(n);
   for i:=1 to n do read(a[i]);
   fillchar(ans,sizeof(ans),0);
   fillchar(p2,sizeof(p2),0);
   p2[1].len:=1;
   p2[1].d[1]:=2;
   multipy;
   work;
   pri;
   close(input);
   close(output);


end.

 

Monster (贪心)

Monster

【题目描述】

明明的手机上有这样一个游戏,有一排n个怪物,每个怪物的血量是mi。现在明明可以射出k个伤害均为p的火球射向某些怪物。当某个火球射到第i个怪物,除了这个怪物会掉血p以外,它左边的第j个怪物(j<=i),也会遭到Max(0, p - (i - j)* (i - j))的溅射伤害。当某个怪物的血量为负的时候,它就死了,但它的尸体已然存在,即其他怪物不会因为它死而改变位置。

明明想用这k 个火球消灭掉所有的怪物,但他同时希望每个火球的伤害p能尽可能的小,这样他才能完美过关。

【输入数据】

第一行两个数 nk (1≤ n ≤ 50000, 1 ≤ k ≤ 100000)。

第二行n个数m1m2,...mn (1 ≤ mi ≤ 109),表示每个怪物的生命值。

【输出数据】

最小的符合要求的p值。

【样例输入】

3 1

1 4 5

【样例输出】

6

【数据范围】

1 ≤ n ≤ 50000, 1 ≤ k ≤100000,1 ≤ mi ≤109

30%的数据,n ≤ 500

贪心+二分 显然 最右边那个妖怪一定是被轰击致死的

Program monster;
const
   INF=1000000000;
var
  n,k,i,j:longint;
  m1,a:array[1..50000] of longint;
procedure sort(l,r:longint);
var
   i,j,m,k2,kn:longint;
begin
   if l=r then
   begin
      writeln(l);
      close(input);
      close(output);
      halt;

   end;

   m:=(l+r) shr 1;
   for i:=1 to n do a[i]:=m1[i];
   k2:=k;
   for i:=n downto 1 do
      if a[i]>0 then
      begin
         kn:=k2;
         dec(k2,a[i] div m);
         a[i]:=a[i] mod m;
         if a[i]>0 then
         begin
            dec(k2);
            dec(a[i],m);
         end;

         if k2<0 then sort(m+1,r);
         kn:=kn-k2;
         j:=i-1;
         while (m-sqr(i-j)>0) and (j>=1) do
         begin
            if (kn*(a[j]-m+sqr(i-j))<0) then a[j]:=0 else
            dec(a[j],kn*(m-sqr(i-j)));
            dec(j);
         end;

      end;
   sort(l,m);


end;
begin
   assign(input,'monster.in');
   assign(output,'monster.out');
   reset(input);
   rewrite(output);

   read(n,k);
   for i:=1 to n do
   begin
      read(m1[i]);
      inc(m1[i]);
   end;


   sort(1,inf+1);


end.

POJ 3748(C++的16进制读法 %x)

P党写几小时的程序 C++才几行……

首先P的位运算有上限2^30 此时 即便是 int64也会因为补码坑死人的

到1 shl 31时   int64 是负数 故 这个时候 不能shr 为多出好多位

造成以上结果的真正原因是 shl 和 shr 只支持到1 shl 30 (Longint)所以在int64或qword会出错 要自己写


C党读入方法 %x 表示 二进制

#include<cstdio>
using namespace std;
int r,x,y;
int main()
{
	scanf("%x,%d,%d",&r,&x,&y);
	r=r&(~(1<<x));
	r=r&(~(1<<y-2));
	r=r|(1<<(y-1))|(1<<(y));
	printf("%xn",r);


}

P党取到30的做法:

Program P3748;
const
// ordA=65;
   orda=97;
   ord0=48;
var
   s:string;
   r:qword;
   x,y:longint;
function turn2(c:char):longint;
begin
   if ord(c)>=orda then begin exit(ord(c)-orda+10); end
   else exit(ord(c)-ord0);
end;
function turn16(x:qword):longint;
begin
   if (x<>0) then
   begin
      turn16:=x and 15;
      x:=x shr 4;
      turn16(x);
      if turn16<=10 then write(turn16) else write(chr(turn16-10+orda));
   end;
end;
function cin:longint;
var
   i:longint;
   s1:string;
begin
   i:=1;
   while s[i]<>',' do inc(i);
   s1:=copy(s,1,i-1);
   val(s1,cin);
   delete(s,1,i);


end;

function cin16:qword;
var
   i,j:longint;
   s1:string;
begin
   i:=1;
   while s[i]<>',' do inc(i);
   s1:=copy(s,1,i-1);
   cin16:=0;
   for j:=1 to i-1 do
      cin16:=cin16 shl 4+turn2(s1[j]);
   delete(s,1,i);
end;


begin
   readln(s);
   r:=cin16;x:=cin;val(s,y);


   r:=r and (not (1 shl x));

   r:=r and (not (1 shl (y-2)));
   r:=r or (1 shl (y-1));
   r:=r or (1 shl y);
   turn16(r);
   writeln;

end.

更正AC版:

Program P3748;
const
// ordA=65;
   orda=97;
   ord0=48;
var
   s:string;
   r,k:qword;
   x,y,i:longint;
function turn2(c:char):longint;
begin
   if ord(c)>=orda then begin exit(ord(c)-orda+10); end
   else exit(ord(c)-ord0);
end;
function turn16(x:qword):longint;
begin
   if (x<>0) then
   begin
      turn16:=x and 15;
      x:=x div 16;
      turn16(x);
      if turn16<=10 then write(turn16) else write(chr(turn16-10+orda));
   end;
end;
function cin:longint;
var
   i:longint;
   s1:string;
begin
   i:=1;
   while s[i]<>',' do inc(i);
   s1:=copy(s,1,i-1);
   val(s1,cin);
   delete(s,1,i);
end;
function cin16:qword;
var
   i,j:longint;
   s1:string;
begin
   i:=1;
   while s[i]<>',' do inc(i);
   s1:=copy(s,1,i-1);
   cin16:=0;
   for j:=1 to i-1 do
      cin16:=cin16 shl 4+turn2(s1[j]);
   delete(s,1,i);
end;

function shl2(x:longint):qword;
var
   i:longint;
begin
   shl2:=1;
   for i:=1 to x do shl2:=shl2*2;

end;
begin
   readln(s);
   r:=cin16;x:=cin;val(s,y);


  r:=r and (not (shl2(x)));


   r:=r and (not (shl2(y-2)));

   r:=r or (shl2(y-1));
   r:=r or (shl2(y));
   turn16(r);
   writeln;

end.

附:

    功能              |           示例            |    位运算
----------------------+---------------------------+--------------------
去掉最后一位          | (101101->10110)           | x shr 1
在最后加一个0         | (101101->1011010)         | x shl 1
在最后加一个1         | (101101->1011011)         | x shl 1+1
把最后一位变成1       | (101100->101101)          | x or 1
把最后一位变成0       | (101101->101100)          | x or 1-1
最后一位取反          | (101101->101100)          | x xor 1
把右数第k位变成1      | (101001->101101,k=3)      | x or (1 shl (k-1))
把右数第k位变成0      | (101101->101001,k=3)      | x and not (1 shl (k-1))
右数第k位取反         | (101001->101101,k=3)      | x xor (1 shl (k-1))
取末三位              | (1101101->101)            | x and 7
取末k位               | (1101101->1101,k=5)       | x and (1 shl k-1)
取右数第k位           | (1101101->1,k=4)          | x shr (k-1) and 1
把末k位变成1          | (101001->101111,k=4)      | x or (1 shl k-1)
末k位取反             | (101001->100110,k=4)      | x xor (1 shl k-1)
把右边连续的1变成0    | (100101111->100100000)    | x and (x+1)
把右起第一个0变成1    | (100101111->100111111)    | x or (x+1)
把右边连续的0变成1    | (11011000->11011111)      | x or (x-1)
取右边连续的1         | (100101111->1111)         | (x xor (x+1)) shr 1
去掉右起第一个1的左边 | (100101000->1000)         | x and (x xor (x-1))