bzoj2634

Description


蒟蒻kAc要从QD去FJ膜拜神犇CP和YY。地图是一个n*m的方格图。QD在右上角,FJ在右下角。
问从QD到FJ有多少条哈密尔顿路?

Input

若干行 每行两个正整数 n m 表示一组测试数据

Output

行数同输入 每行一个整数 表示每组测试数据的答案 模7777777 ,无解输出"YM CP&YY"

Sample Input

3 2
3 3
4 10

Sample Output

YM CP&YY
2
2329

HINT



N <= 7 M <= 10^9 组数T <= 20

不卡常数




一道很有意思的原创题。

实际上这类问题可以用插头dp解决。

但是这个m非常大,逐格转移时间不能接受。

但是插头dp主要是来解决有障碍的图的。

所以对于这道题,由于没有障碍,可以用矩阵把转移表示出来,然后快速幂就可以了。

但是插头dp是逐格转移的,在行的变换之时会状态会有所变化。

所以设计逐行转移的矩阵。

就是枚举每个状态为初始状态,进行一行的插头dp,求从一个行状态转移到另一个行状态的转移方程。

插头dp每个状态由一个括号序列表示,虽然用三进制表示,但是7位的合法括号序列只有127种。

127^3*log 10^9在6000万左右,加上矩阵乘法的优化,就得出了一个可行的算法。


对于这类问题我进行了更一步的思考。

如果在n行m列的情况下,n是可以支持状压dp的数值,m很大,有k个障碍。

那么实际上只需对有障碍的行进行状压dp,在两个有障碍的行之间进行矩阵乘法加速,那么复杂度为O(状态数^3*log m * k),对时间要求不是很高的话,可以在状态数较少,k值较大的情况下求解。


program bzoj2634;
const
  maxn=7777777;
type
  mat=array [0..127,0..127] of longint;
var
  n,m,i,j,k,len,o,stu,x,y,w,sum,z:longint;
  tot:array [0..1] of longint;
  dl,f:array [0..1,0..8000] of longint;
  yes:array [0..8000] of boolean;
  ans,root:mat;
  thr:array [0..9] of longint;
  map:array [0..2188] of longint;

procedure mul (var a,b:mat);inline;
var
  i,j,k:longint;
  ans:mat;
begin
  fillchar(ans,sizeof(ans),0);
  for i:=1 to sum do
    for k:=1 to sum do
      if a[i,k]<>0 then
        for j:=1 to sum do
          ans[i,j]:=(ans[i,j]+int64(a[i,k])*b[k,j]) mod maxn;
  a:=ans;
end;

begin
  thr[0]:=1;
  for i:=1 to 9 do
   thr[i]:=thr[i-1]*3;
  while not eof do
    begin
      readln(n,m);
      if (n and 1 = 1)and(m and 1 = 0) then
        begin
          writeln('YM CP&YY');
          continue;
        end;
      sum:=0;
      for i:=0 to thr[n]-1 do
        begin
          x:=0;
          y:=0;
          k:=i;
          while k<>0 do
            begin
              if k mod 3 = 1 then inc(x);
              if k mod 3 = 2 then inc(y);
              if y>x then break;
              k:=k div 3;
            end;
          if x<>y then continue;
          inc(sum);
          map[i]:=sum;
        end;
      fillchar(root,sizeof(root),0);
      for k:=1 to thr[n]-1 do
        if map[k]<>0 then
          begin
            o:=0;
            fillchar(f[o],sizeof(f[o]),0);
            tot[o]:=1;
            f[o,3*k]:=1;
            dl[o,1]:=3*k;
            for i:=1 to n do
              begin
                o:=1-o;
                fillchar(f[o],sizeof(f[o]),0);
                fillchar(yes,sizeof(yes),false);
                tot[o]:=0;
                for j:=1 to tot[1-o] do
                  begin
                    stu:=dl[1-o,j];
                    x:=stu div thr[i-1] mod 3;
                    y:=stu div thr[i] mod 3;
                    stu:=stu-x*thr[i-1]-y*thr[i];
                    if (i=n)and(x=1)and(y=2)and(stu=0) then
                      begin
                        f[o,0]:=f[o,0]+f[1-o,dl[1-o,j]];
                        if not yes[0] then
                          begin
                            yes[0]:=true;
                            inc(tot[o]);
                            dl[o,tot[o]]:=0;
                          end;
                      end;
                    if (x=1)and(y=2) then continue;
                    if (x=0)and(y=0) then
                      begin
                        w:=stu+thr[i-1]+thr[i]*2;
                        f[o,w]:=f[o,w]+f[1-o,dl[1-o,j]];
                        if not yes[w] then
                          begin
                            yes[w]:=true;
                            inc(tot[o]);
                            dl[o,tot[o]]:=w;
                          end;
                        continue;
                      end;
                    if x=0 then
                      begin
                        w:=stu+thr[i-1]*y;
                        f[o,w]:=f[o,w]+f[1-o,dl[1-o,j]];
                        if not yes[w] then
                          begin
                            yes[w]:=true;
                            inc(tot[o]);
                            dl[o,tot[o]]:=w;
                          end;
                        w:=stu+thr[i]*y;
                        f[o,w]:=f[o,w]+f[1-o,dl[1-o,j]];
                        if not yes[w] then
                          begin
                            yes[w]:=true;
                            inc(tot[o]);
                            dl[o,tot[o]]:=w;
                          end;

                        continue;
                      end;
                    if y=0 then
                      begin
                        w:=stu+thr[i-1]*x;
                        f[o,w]:=f[o,w]+f[1-o,dl[1-o,j]];
                        if not yes[w] then
                          begin
                            yes[w]:=true;
                            inc(tot[o]);
                            dl[o,tot[o]]:=w;
                          end;
                        w:=stu+thr[i]*x;
                        f[o,w]:=f[o,w]+f[1-o,dl[1-o,j]];
                        if not yes[w] then
                          begin
                            yes[w]:=true;
                            inc(tot[o]);
                            dl[o,tot[o]]:=w;
                          end;
                        continue;
                      end;
                    if (x=2)and(y=1) then
                      begin
                        w:=stu;
                        f[o,w]:=f[o,w]+f[1-o,dl[1-o,j]];
                        if not yes[w] then
                          begin
                            yes[w]:=true;
                            inc(tot[o]);
                            dl[o,tot[o]]:=w;
                          end;
                        continue;
                      end;
                    if x=1 then
                      begin
                        len:=1;
                        z:=i;
                        repeat
                          inc(z);
                          if stu div thr[z] mod 3 = 1 then
                            inc(len);
                          if stu div thr[z] mod 3 = 2 then
                            dec(len);
                        until len=0;
                        w:=stu-thr[z];
                      end
                           else
                      begin
                        len:=-1;
                        z:=i-1;
                        repeat
                          dec(z);
                          if stu div thr[z] mod 3 = 1 then
                            inc(len);
                          if stu div thr[z] mod 3 = 2 then
                            dec(len);
                        until len=0;
                        w:=stu+thr[z];
                      end;
                    f[o,w]:=f[o,w]+f[1-o,dl[1-o,j]];
                    if not yes[w] then
                      begin
                        yes[w]:=true;
                        inc(tot[o]);
                        dl[o,tot[o]]:=w;
                      end;
                  end;
              end;
            for i:=0 to thr[n]-1 do
              if map[i]<>0 then
                root[map[i],map[k]]:=root[map[i],map[k]]+f[o,i];
          end;
      fillchar(ans,sizeof(ans),0);
      for i:=1 to sum do
        ans[i,i]:=1;
      while m>0 do
        begin
          if m and 1 = 1 then mul(ans,root);
          m:=m div 2;
          mul(root,root);
        end;
      stu:=map[1+thr[n-1]*2];
      writeln(ans[1,stu]);
    end;
end.


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
BZOJ 2908 题目是一个数据下载任务。这个任务要求下载指定的数据文件,并统计文件中小于等于给定整数的数字个数。 为了完成这个任务,首先需要选择一个合适的网址来下载文件。我们可以使用一个网络爬虫库,如Python中的Requests库,来帮助我们完成文件下载的操作。 首先,我们需要使用Requests库中的get()方法来访问目标网址,并将目标文件下载到我们的本地计算机中。可以使用以下代码实现文件下载: ```python import requests url = '目标文件的网址' response = requests.get(url) with open('本地保存文件的路径', 'wb') as file: file.write(response.content) ``` 下载完成后,我们可以使用Python内置的open()函数打开已下载的文件,并按行读取文件内容。可以使用以下代码实现文件内容读取: ```python count = 0 with open('本地保存文件的路径', 'r') as file: for line in file: # 在这里实现对每一行数据的判断 # 如果小于等于给定整数,count 加 1 # 否则,不进行任何操作 ``` 在每一行的处理过程中,我们可以使用split()方法将一行数据分割成多个字符串,并使用int()函数将其转换为整数。然后,我们可以将该整数与给定整数进行比较,以判断是否小于等于给定整数。 最后,我们可以将统计结果打印出来,以满足题目的要求。 综上所述,以上是关于解决 BZOJ 2908 数据下载任务的简要步骤和代码实现。 希望对您有所帮助。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值