[BZOJ3177][Coci2012][压位][阈值]Skakac

考虑 f[T][x][y] 表示在T时刻是否能到达 (x,y) ,那么就可以 O(Tn2) 的时间内完成转,因为n<=30,就可以把一列的状态压成一个整数,每次转移就可以通过位运算实现,这样如果没有障碍物,转移就可以 O(Tn)
接下来就考虑障碍物的情况,如果 Ki,j>1000 那么把在T以内的 Ki,j 的倍数记录下来,当T达到这个值的时候处理。
如果 Ki,j<=1000 ,可以把 Ki,j 分解质因数,设 Ki,j=pc11pc22...pcnn
那么当T能同时整除 pc11 , pc22, pcnn
g[i][j] 表示能整除 pji 时棋盘的情况,
那么把T也分解质因数,棋盘的情况就可以表示为
g[1][c1] and g[2][c2]...and g[n][cn]
当然次数为0的也要考虑进去,为了节省时间可以令 f[i][j]=g[i][0] and g[i+1][0] ... and g[j][0]
分解质因数可以用 O(n) 预处理, O(logn) 分解的方法。
其实也可以用O(sqrt(n))
就可以A啦

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <vector>
#define N 35
#define M 1000010

using namespace std;

int x,y,n,t,now,pp,cnt;
int A[N][N];
int f[2][N];
int p[M],v[M];
int g[210][21][N];
int h[210][210][N];
int w[N];
pair<int,int> fc[M];

struct stp{
  int t,x,y;
  stp(int a=0,int b=0,int c=0):t(a),x(b),y(c){}
  friend bool operator <(stp a,stp b){ return a.t<b.t; }
};
vector<stp> V;
vector<pair<int,int> > Ans;

inline char nc(){
  static char buf[100000],*p1=buf,*p2=buf;
  return p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++;
}

inline void rea(int &x){
  char c=nc(); x=0;
  for(;c>'9'||c<'0';c=nc());for(;c>='0'&&c<='9';x=x*10+c-'0',c=nc());
}

inline void First(){
  for(int i=2;i<=M-10;i++){
    if(!p[i]) p[++*p]=i,v[i]=*p;
    for(int j=1;j<=*p&&1ll*i*p[j]<=M-10;j++){
      p[i*p[j]]=1;
      v[i*p[j]]=j;
      if(i%p[j]==0) break;
    }
  }
  for(int i=1;i<=*p;i++)
    if(p[i]<=1000) pp=i; else break;
}

inline void Add(int k,int x,int y){
  for(int i=1;i<=pp;i++){
    int c=0;
    while(k%p[i]==0) c++,k/=p[i];
    for(;c<=20;c++) g[i][c][x]|=1<<y;
  }
}

inline void fac(int x){
  cnt=0;
  while(x^1){
    int c=0,t=v[x];
    while(x%p[t]==0) c++,x/=p[t];
    fc[++cnt]=make_pair(t,c);
  }
  sort(fc+1,fc+1+cnt);
  int lst=1;
  for(int i=1;i<=cnt&&fc[i].first<=pp;i++){
    for(int j=1;j<=n;j++) w[j]&=g[fc[i].first][fc[i].second][j];
    if(lst<fc[i].first) for(int j=1;j<=n;j++) w[j]&=h[lst][fc[i].first-1][j];
    lst=fc[i].first+1;
  }
  if(lst<pp) for(int i=1;i<=n;i++) w[i]&=h[lst][pp][i];
}

int main(){
  rea(n); rea(t); First();
  rea(x); rea(y);
  for(int i=1;i<=n;i++)
    for(int j=1;j<=n;j++){
      rea(A[i][j]);
      if(A[i][j]>=1000)
    for(int k=1;k*A[i][j]<=t;k++) V.push_back(stp(k*A[i][j],i,j));
      else Add(A[i][j],i,j);
    }
  sort(V.begin(),V.end());
  for(int i=1;i<=pp;i++)
    for(int j=i;j<=pp;j++)
      for(int s=1;s<=n;s++){
    h[i][j][s]=(1<<n+1)-1;
    for(int k=i;k<=j;k++)  
      h[i][j][s]&=g[k][0][s];
      }
  f[0][x]|=1<<y;
  /*printf("#%d:\n",0);
  for(int i=1;i<=n;i++,putchar('\n'))
  for(int j=1;j<=n;j++) printf("%d ",f[0][i]>>j&1);*/
  for(int i=1,P=0;i<=t;i++){
    now^=1;
    for(int j=1;j<=n;j++) f[now][j]=0,w[j]=(1<<n+1)-1;
    for(int j=1;j<=n;j++){
      if(j>1) f[now][j]|=(f[now^1][j-1]>>2)|(f[now^1][j-1]<<2);
      if(j<n) f[now][j]|=(f[now^1][j+1]>>2)|(f[now^1][j+1]<<2);
      if(j>2) f[now][j]|=(f[now^1][j-2]>>1)|(f[now^1][j-2]<<1);
      if(j<n-1) f[now][j]|=(f[now^1][j+2]>>1)|(f[now^1][j+2]<<1);
    }
    fac(i);
    while(P<V.size()&&V[P].t==i) w[V[P].x]|=(1<<V[P].y),P++;
    for(int j=1;j<=n;j++) f[now][j]&=w[j];
    /* printf("#w%d:\n",i);
    for(int j=1;j<=n;j++,putchar('\n'))
      for(int k=1;k<=n;k++)
    printf("%d ",w[j]>>k&1);
    printf("#%d:\n",i);
    for(int j=1;j<=n;j++,putchar('\n'))
      for(int k=1;k<=n;k++)
      printf("%d ",f[now][j]>>k&1);*/
  }
  for(int i=1;i<=n;i++)
    for(int j=1;j<=n;j++)
      if(f[now][i]&(1<<j)) Ans.push_back(make_pair(i,j));
  printf("%d\n",Ans.size());
  for(int i=0;i<Ans.size();i++) printf("%d %d\n",Ans[i].first,Ans[i].second);
  return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值