codeforces 715D. Create a Maze

题目链接http://codeforces.com/problemset/problem/715/D
题目大意:在n*m的房间中,相邻的两间房间之间有门,每次可以向右或向下走(如果门开着),求构造出一种方案,使得从(1,1)走到(n,m)恰好有T种走法。输出第一行两个整数n和m,第二行一个整数k,接下来k行每行四个整数x1,y1,x2,y2,表示(x1,y1)和(x2,y2)之间的门关着。
数据范围:1 ≤ T ≤ 10^18
输出要求:1 ≤ n, m ≤ 50,0 ≤ k ≤ 300

题解:首先说一种并不能AC的做法(被拍飞)。
假设我们当前已经构造出了一个n*m的矩阵,并且从(1,1)走到(n,m)的方案数是t,而走到(n-1,m+1)(如果存在的话)的方案是1,考虑如何构造出2t和2t+1。见下图:

这里写图片描述

这样每t增加一倍,行数和列数各加1(右上的1只有当前t还没有达到T时需要,如果t=T,那么那一列是不需要的),我们只需要对T进行二进制分解,分情况讨论一下方案数*2后是否加1就可以了。此时n,m最大可以达到60,而题目要求n,m<=50,因此这个方法无法通过全部数据。

由于上述方法以2为底时,n和m超过了50,所以我们考虑选择其它数字作为底数。因为我们只维护当前右下角的方案数为t,所以每次只增加一行一列时,最多只能增加到2t;而增加一行两列或两行一列时,右下角可以达到3t,此时n,m大约是3/2*log(3)T=57;当增加两行两列时,右下角可以达到6t,此时n,m约等于2*log(6)T=48,满足条件。
于是我们构造出下列方法:

这里写图片描述

维护当前的(n,n)方案为t,(n-1,n+1),(n-1,n+2),(n+1,n-1),(n+2,n-1)的方案是1。
初始时n=2,关上(1,2)(2,2)和(2,1)(2,2)之间的门,使得(2,2)的方案数为0。然后每次行、列加2,方案数乘6+x(x为T的六进制分解在当前位上的数字)直到得到T为止。
n=2+2*log(6)T=2+2*24=50,k<=2+12*log(6)T=290。
时间复杂度O(logT)

代码如下:

#include <algorithm>
#include <cstdio>
struct P{
    int x1,y1,x2,y2;
    void PRINT(){printf("%d %d %d %d\n",x1,y1,x2,y2);}
}ans[400];
int main(){
    long long n;
    scanf("%I64d\n",&n);
    int m=0,k=2,len=2,a[30];
    for (;n;n/=6) a[++m]=n%6;
    ans[1]=(P){1,2,2,2};
    ans[2]=(P){2,1,2,2};
    for (int i=m;i>=1;i--){
        if (a[i]<=2) ans[++k]=(P){len-1,len+1,len,len+1};
        if (a[i]%3==0) ans[++k]=(P){len-1,len+2,len,len+2};
        ans[++k]=(P){len+1,len-1,len+1,len};
        if (a[i]%3!=2) ans[++k]=(P){len+2,len-1,len+2,len};
        len+=2;
        if (i==1) break;
        ans[++k]=(P){len-2,len,len-2,len+1};
        ans[++k]=(P){len-1,len,len-1,len+1};
        ans[++k]=(P){len-2,len+2,len-1,len+2};
        ans[++k]=(P){len,len-2,len+1,len-2};
        ans[++k]=(P){len,len-1,len+1,len-1};
        ans[++k]=(P){len+2,len-2,len+2,len-1};
        if (len<=4) continue;
        ans[++k]=(P){len-4,len+1,len-3,len+1};
        ans[++k]=(P){len+1,len-4,len+1,len-3};
    }
    printf("%d %d\n%d\n",len,len,k);
    for (int i=1;i<=k;i++) ans[i].PRINT();
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值