bzoj 5122: A

update

被标解虐飞了QAQ。。虽然我不知道为什么..
这里写图片描述
但还是蛮开心的,因为之前有过一个想法,就是要尽量做到做题时就算有结论没有猜出,或者想歪了,也能用一个哪怕复杂一点的方法做出来

前言

昨天晚上写了好像从八点多写到了十点。。
感觉细节很多。。写得很慢,感觉写得很稳,把能稳的都写稳了。。
于是昨晚成功在回宿舍前被卡常。。
今天早上怒卡了一波常数才过。。

题解

这里提供一个不一定是正解的做法吧。。因为看起来实在和别人差蛮多的。。
首先,我们可以用数学知识算出来他最后在哪一个点
也就是算一下他往上走了多少步,往下走了多少步,往左走了多少步,往右走了步,然后加加减减就可以了。。
然后我们考虑倒着走回去,那么这个时候,如果一个点如果之前被走过了,那么他是肯定不会被染色的,对吧
所以我们每一次就只选择没有走过的点,直接跳过去
这个功能可以对于每一个点用四个并查集来维护,分别是向上,向下,向左,向右,下一个没有染过色的是哪里
然后每一次就看一看在在当前的 stepSize 够不够走到那个个格,如果够,就直接跳过去,否则就算一个最后走到哪里就可以了
然后如果对于一个行,全部被染过了,那么就直接算一下最后到哪里,跳一下就可以了
然后最后如果全部格子都被染色了,就退出了
因为我们考虑到每一次他都会转向,然后走的步数多且每次不一样,所以事实上很快地图就会被填完
所以期望时间复杂度是 O(rc)
但是可能常数会有点大。。所以我卡了好一会的常数才过。还有要注意的是,输出最好用putchar,在我用printf的时候,本机 9 10s ,改了时候 1s 不到就跑出来了

CODE:
(因为怕写错,所以我全部没有用偏移量+for,而是选择了认真地手写了4种情况,所以代码很长。。)

#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<cstring>
#include<iostream>
using namespace std;
typedef long long LL;
const int N=2005;
int r,c,i,j;
LL n;//地图范围   出生点    走了多少步 
LL A,B,C,D;//向上走了多少步   向右走了多少步   向下走了多少步 向左走了多少步 
LL E[4];
LL F[4];
void prepare ()
{
    //A=1+5+9+13+17+.....E[0]
    //B=2+6+10+14+.....+E[1]
    //C=3+7+11+15+...+E[2]
    //D=4+8+12+16+....+E[3]
    for (LL u=0;u<=3;u++)
    {
        LL x=n-u;
        if (x%4==0) E[3]=x;
        if (x%4==1) E[0]=x;
        if (x%4==2) E[1]=x;
        if (x%4==3) E[2]=x;
    }
    F[0]=(E[0]-1)/4+1;
    F[1]=(E[1]-2)/4+1;
    F[2]=(E[2]-3)/4+1;
    F[3]=(E[3]-4)/4+1;
    /*if (E[0]>0) A=(1+E[0])*(F[0])/2;
    if (E[1]>0) B=(2+E[1])*(F[1])/2;
    if (E[2]>0) C=(3+E[2])*(F[2])/2;
    if (E[3]>0) D=(4+E[3])*(F[3])/2;*/
    if (E[0]>0) 
    {
        if (F[0]%2==0)  A=((1+E[0])%r*((F[0]/2)%r))%r;
        else A=(((1+E[0])/2)%r*(F[0]%r))%r;
    }
    if (E[1]>0) 
    {
        if (F[1]%2==0)  B=((2+E[1])%c*((F[1]/2)%c))%c;
        else B=(((2+E[1])/2)%c*(F[1]%c))%c;
    }
    if (E[2]>0) 
    {
        if (F[2]%2==0)  C=((3+E[2])%r*((F[2]/2)%r))%r;
        else C=(((3+E[2])/2)%r*(F[2]%r))%r;
    }
    if (E[3]>0) 
    {
        if (F[3]%2==0)  D=((4+E[3])%c*((F[3]/2)%c))%c;
        else D=(((4+E[3])/2)%c*(F[3]%c))%c;
    }
    A=A-C;//向上走了多少步
    B=B-D;//向右走了多少步
    i=i-A;j=j+B;
    if (i>r)    {i%=r;if (i==0) i=r;}
    else if (i<=0) i=i%r+r;
    if (j>c)    {j%=c;if (j==0) j=c;}
    else if (j<=0) j=j%c+c;
    //while (i>r) i-=r;while (i<=0) i+=r;
    //while (j>c) j-=c;while (j<=0) j+=r;
}
int g[N][N];//这个点是什么状态  0:@  其他就是字母 
int f[4][N][N];//往上的并查集   往下的并查集   往左的并查集   往右的并查集   这个点下一个有用的是什么 
int find01(LL op,LL x,LL y){return f[op][x][y]==x?x:f[op][x][y]=find01(op,f[op][x][y],y);}
int find23(LL op,LL x,LL y){return f[op][x][y]==y?y:f[op][x][y]=find23(op,x,f[op][x][y]);}
int xx[N],yy[N];//第i行,或者第j列删剩了多少 
int tot=0;//一共删了多少个点
void Del (LL x,LL y)
{
    tot++;
    xx[x]++;yy[y]++;
    f[0][x][y]--;if (f[0][x][y]<1) f[0][x][y]+=r;
    f[1][x][y]++;if (f[1][x][y]>r) f[1][x][y]-=r;
    f[2][x][y]--;if (f[2][x][y]<1) f[2][x][y]+=c;
    f[3][x][y]++;if (f[3][x][y]>c) f[3][x][y]-=c;
}
LL dis1 (int x,int y)//横着x走到y的距离 
{
    if (x<=y) return y-x;
    return y+c-x;
}
LL dis0 (int x,int y)//竖着x走到y的距离
{
    if (x<=y) return y-x;
    return y+r-x;
}
void solve ()
{
    memset(xx,0,sizeof(xx));memset(yy,0,sizeof(yy));
    int d,col;
    LL now;//当前的方向是什么     1:向上走    2:向右走    3:往下走   0:向左走 
    //当然,方向要反过来 
    col=n%26;
    d=n%4;
    for (LL u=n;u>=1;u--)//模拟过程,那么这个点就要走这个多步 
    {

        if (tot==r*c) break;
        LL now=u;//还要走多少步 
        if (col==0) col=26;
        if (d==-1) d=3; 
        if (d==0)//向右走
        {
            while (true)
            {
                if (xx[i]==c)
                {
                    j=j+now%c;//到达这个点
                    j=j%c;if (j==0) j=c;
                    //while (j>r) j-=r;
                    break;
                }
                int jj=find23(3,i,j);//找到下一个
                if (dis1(j,jj)>now)//不够走过去
                {
                    j=j+now%c;
                    j=j%c;if (j==0) j=c;
                    //while (j>r) j-=r;
                    break;
                }
                now=now-dis1(j,jj);
                j=jj;
                g[i][j]=col;
                Del(i,j);
            }
        }
        if (d==1)//向下走
        {
            while (true)
            {
                if (yy[j]==r)
                {
                    i=i+now%r;
                    i=i%r;if (i==0) i+=r;
                    //while (i>c) i-=c;
                    break;
                }
            //  printf("%lld ",yy[j]);
                int ii=find01(1,i,j);
            //  printf("new_i:%lld\n",yy[j]);
                if (dis0(i,ii)>now)//不够
                {
                    i=i+now%r;
                    i=i%r;if (i==0) i+=r;
                    //while (i>c) i-=c;
                    break;
                }
                now=now-dis0(i,ii);
                i=ii;
                g[i][j]=col;
                Del(i,j);
            //  printf("%lld ",f[1][i][j]);
            }
        }
        if (d==2)//向左走
        {
            while (true)
            {
                if (xx[i]==c)
                {
                    j=j-now%c;//到达这个点
                    //while (j<=0) j+=r;
                    if (j<=0) j=j%c+c;
                    break;
                }
                int jj=find23(2,i,j);//找到下一个
                if (dis1(jj,j)>now)//不够走过去
                {
                    j=j-now%c;
                    //while (j<=0) j+=r;
                    if (j<=0) j=j%c+c;
                    break;
                }
                now=now-dis1(jj,j);
                j=jj;
                g[i][j]=col;
                Del(i,j);
            }
        }
        if (d==3)//向上走
        {
            while (true)
            {
                //  printf("%d %d\n",i,j);
                if (yy[j]==r)
                {
                    i=i-now%r;
                    if (i<=0) i=i%r+r;
                    break;
                }
                int ii=find01(0,i,j);
                if (dis0(ii,i)>now)//不够
                {
                    i=i-now%r;
                    //while (i<=0) i+=c;
                    if (i<=0) i=i%r+r;
                    break;
                }
                now=now-dis0(ii,i);
                i=ii;
                g[i][j]=col;
                Del(i,j);
            }
        }
        col--;
        d--;
    }
}
int main()
{
//  freopen("a.in","r",stdin);
//  freopen("a.out","w",stdout);
    scanf("%d%d%d%d%lld",&r,&c,&i,&j,&n);
    prepare();
    //  printf("NO:%d %d\n",i,j);
    //printf("n:%lld\n",n);
    memset(g,-1,sizeof(g));
    int I=i,J=j;
    for (int u=1;u<=r;u++)
        for (int i=1;i<=c;i++)
        {
            f[0][u][i]=f[1][u][i]=u;
            f[2][u][i]=f[3][u][i]=i;
        }
    //printf("YES:%d %d\n",i,j);
    solve();
    g[I][J]=0;
    for (int u=1;u<=r;u++)
    {
        for (int i=1;i<=c;i++)
        {
            if (g[u][i]==-1) putchar('.');
            else if (g[u][i]==0) putchar('@');
            else putchar(g[u][i]-1+'A'); 
        }
        putchar('\n');
    }
    return 0;
}   
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值