COCI2011/2012 Contest#1 F Solution

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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章