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