COCI2011/2012 Contest#1 F Solution

本文介绍了一个基于棋盘的游戏——骑士游戏的解决方案。游戏要求玩家预测骑士在特定规则下的最终位置。文章提出了一种O(n*T)的时间复杂度算法,通过巧妙处理棋盘上每个格子的开放时间来解决这一问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Description

Mirko and Slavko are playing the popular new game known as The Knight. Mirko places a knight chess piece on an NxN chessboard and, with Slavko blindfolded, makes exactly T moves, one per second.
After that, Slavko must guess the final position of the knight in order to win.
The chessboard in this game is unusual in that each square is blocked part of the time. More precisely, each square is labelled by a positive integer. A square labelled by number K is clear only during seconds 0, K, 2K, 3K etc; it is blocked at all other times. The knight can, of course, occupy a square only while the square is clear. The game begins in second 0. In each second Mirko must make a move (selecting one of 8 possible Lshaped moves, two squares in one direction and one square in the other, as per standard chess rules), provided that the destination square is not blocked during the next second. Help Slavko by writing a program to calculate all possible squares that the knight can possibly occupy after T moves.

Solution

这道题属于历史遗留问题范畴,虽然A完以后觉得只是浪费了大半天,并没有学到什么。
看完题面第一个想法显然是复杂度O(n2T)的暴力,因为常数的原因是会T的。
标程给出了一个O(nT)的做法。
首先对于移动我们可以把它给压位了。
然后对于那些数字,如果它大于1000,以时间轴搞出一个vector,然后在相应的vector里加入这个时间(一句话,暴力)
对于<=1000的数字,暴力显得既会MLE又会TLE,那么我们用一种神奇的方式来处理。
首先1000内有168个素数。
对于数字Ki,j(<=1000),设Ki,j=168i=1pαii,设此刻t=168i=1pβii,若Ki,j | t,则对于αiαi<=βi
由以上性质,我们可以定义一个函数F[t,pγ],表示时刻t,矩阵中的点(i,j)tp的指数为γ。函数的值为(bool)γ>α
那么对于t时刻,格子(i,j)的填充状态可以用以下式子表示:
F[t,pγ11] &F[t,pγ22] &…& F[t,pγ168168]………………………………………(1)
同样的,这个函数也可以压位==
然后直接算好像并不快。但是我们可以观察到对于ti=1αi<=log2(1000),由抽屉原理,不会有超过10个数的幂>0
于是可以把那些幂为0的给前缀和。
那么我们先预处理一个函数G[t,x,y]=F[t,p0x]&F[t,p0x+1]&…&F[t,p0y]
于是上面那个(1),我们可以不超过10次地计算出值。
主代码非常烦,细节非常多,非常恶心==

Code

#include<iostream>
#include<string.h>
#include<stdio.h>
#include<algorithm>
#include<vector>
using namespace std;
const int M=35;
int A[M][M];
#define vec vector<int>
#define pb push_back
const int MAX_T=1e6+5;
const int MAX_P=170;
vec v[MAX_T];
int prime[MAX_P],f[MAX_T],tot[MAX_T],fe[MAX_T],ff[MAX_T];
int F[M][MAX_P][20],G[M][MAX_P][MAX_P];
int t,n,T,x,y;
inline void build(){
    for(int i=2;i*i<MAX_T;++i){
        if(!f[i]){
            for(int j=i<<1;j<MAX_T;j+=i)
                f[j]=i;
            tot[i]=t;
            prime[t++]=i;
        }
    }
    for(int i=2;i<MAX_T;++i){
        if(!f[i])f[i]=i;
        int x=i;
        ff[i]=1;
        for(;x%f[i]==0;)
            x/=f[i],++fe[i],ff[i]*=f[i];
    }
}
inline void modify(int &x,int y){
    x|=(1<<y);
}
inline void pret(){
    for(int i=0;i<n;++i)
        for(int j=0;j<t;++j)
            G[i][j][j]=F[i][j][0];
    for(int i=0;i<n;++i)
        for(int j=0;j<t;++j)
            for(int k=j+1;k<t;++k)
                G[i][j][k]=G[i][j][k-1]&G[i][k][k];
}
int sta[2][M],m[M];
inline bool check(int x,int y){
    return x>>y&1;
}
int main(){
    cin>>n>>T>>x>>y;
    --x,--y;
    build();
    for(int i=0;i<n;++i)
        for(int j=0;j<n;++j){
            scanf("%d",A[i]+j);
            if(A[i][j]>=1000){
                for(int k=A[i][j];k<T;k+=A[i][j])
                    v[k].pb((i<<5)+j);//算是类似hash的东西吧,但是这样不会冲突(无损hash) 
            }
            else{
                int x=A[i][j];
                for(int w=0;w<t;++w){
                    int p=prime[w],c=0;
                    for(;x%p==0;)++c,x/=p;
                    for(;c<20;++c)modify(F[i][w][c],j);
                }
            }
        }
    pret();
    int tot2=(1<<n-2)-1,tot1=(1<<n-1)-1;
    bool cur=1,nxt=0;
    modify(sta[nxt][x],y);
    for(int tm=0;tm<T;++tm){
        nxt^=1,cur^=1;
        memset(sta[nxt],0,sizeof(sta[nxt]));
        for(int i=0;i<n;++i){
            if(i-1>=0)sta[nxt][i]|=(sta[cur][i-1]&tot2)<<2|sta[cur][i-1]>>2;
            if(i-2>=0)sta[nxt][i]|=(sta[cur][i-2]&tot1)<<1|sta[cur][i-2]>>1;
            if(i+2<n)sta[nxt][i]|=(sta[cur][i+2]&tot1)<<1|sta[cur][i+2]>>1;
            if(i+1<n)sta[nxt][i]|=(sta[cur][i+1]&tot2)<<2|sta[cur][i+1]>>2;
            m[i]=(1<<n)-1;
        }//位运算更新答案 
        int x=tm+1,last=t-1;
        for(;x>1&&f[x]<1000;){
            int now=tot[f[x]];
            if(last>now){
                for(int i=0;i<n;++i)
                    m[i]&=G[i][now+1][last];
            }
            for(int i=0;i<n;++i)
                m[i]&=F[i][now][fe[x]];
            x/=ff[x],last=now-1;
        }//分段求和
        if(last>=0)
            for(int i=0;i<n;++i)
                m[i]&=G[i][0][last];
        for(int i=0;i<v[tm+1].size();++i)
            modify(m[v[tm+1][i]/32],v[tm+1][i]%32);
        for(int i=0;i<n;++i)
            sta[nxt][i]&=m[i];
    }
    int res=0;
    for(int i=0;i<n;++i)
        for(int j=0;j<n;++j)
            if(check(sta[nxt][i],j))++res;
    cout<<res<<endl;
    for(int i=0;i<n;++i)
        for(int j=0;j<n;++j)
            if(check(sta[nxt][i],j))
                printf("%d %d\n",i+1,j+1);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值