【学习笔记】[POI2015] CZA

考虑按 1 ∼ n 1\sim n 1n的顺序插入序列。将答案除以 n n n即可。

那么我们唯一要干的事情就是把哪些位置是 固定的 给找出来。

首先,如果一个数小于等于 i − 4 i-4 i4,那么它的旁边都不能插数,可以看作是固定的。并且对于小于等于 i − 4 i-4 i4的数,因为在后续填数的过程中不会影响其左右两边的数,因此在当前状态下一定是合法的。换言之,当填入 i i i过后,我们只需要检验 i − 3 i-3 i3是否合法即可。故而,我们只需要关注 i − 3 , i − 2 , i − 1 i-3,i-2,i-1 i3,i2,i1的相对位置关系,以及是否相邻。这显然可以用最暴力的状压解决,然后就做完了。这玩意用顺逆时针来记录会更简单一些,剩下的全部是手动分讨。

我承认,这个做法非常垃圾。这也导致了代码非常难打。

#include<bits/stdc++.h>
#define ll long long
#define fi first
#define se second
#define pb push_back
#define inf 0x3f3f3f3f
using namespace std;
const int N=1e6+5;
const int mod=1e9+7;
int n,K,D,p[3],ban[N][10],now[2][8],nxt[2][8],res,state[N];
vector<int>vec;
void add(int &x,int y){x=(x+y)%mod;}
int check(){
  vec.clear();
  for(int i=1;i<=n;i++){
    if(!state[i]){
      vec.pb(i);
    }
  }
  for(int i=n;i>=1;i--){
    if(state[i]){
      vec.pb(i);
    }
  }
  for(int i=0;i+1<vec.size();i++){
    if(ban[vec[i]][vec[i+1]-vec[i]+3])return 0;
  }
  if(ban[vec.back()][vec[0]-vec.back()+3])return 0;
  return 1;
}
int check(int x,int y){
  return ban[x][y-x+3];
}
void solve1(int x,int s){
  int sig=s>>1&1;
  if((s&1)&&(!(s&4)||!check(x-1,x-3))&&!check(x-3,x)){
    add(nxt[0][4|sig],now[0][s]);
  }
  if((s&2)&&(!(s&1)||!check(x-3,x-2))&&(!(s&4)||!check(x-1,x-3))){
    add(nxt[1][3],now[0][s]);
  }
  if((s&4)&&(!(s&1)||!check(x-3,x-2))&&!check(x,x-3)){
    add(nxt[0][2|sig],now[0][s]);
  }
}
void solve2(int x,int s){
  int sig=s>>1&1;
  if((s&1)&&(!(s&4)||!check(x-2,x-3))&&!check(x-3,x)){
    add(nxt[1][2|(sig<<2)],now[1][s]);
  }
  if((s&2)&&(!(s&1)||!check(x-3,x-1))&&(!(s&4)||!check(x-2,x-3))){
    add(nxt[0][6],now[1][s]);
  }
  if((s&4)&&(!(s&1)||!check(x-3,x-1))&&!check(x,x-3)){
    add(nxt[1][1|(sig<<2)],now[1][s]);
  }
}
int solve(){
  if(D==0){
    if(n==1)return 1;
    return 0;
  }
  if(D==1){
    if(n==1)return 1;
    else if(n==2){
      if(ban[1][2-1+3]||ban[2][1-2+3])return 0;
      return 1;
    }
    else{
      return 0;
    }
  }
  if(D==2){
    //最多只有两种情况
    if(n==1)return 1;
    else if(n==2){
      if(ban[2-1+3]||ban[1-2+3])return 0;
      return 1;
    }
    for(int i=1;i<=n;i+=2)state[i]=0;
    for(int i=2;i<=n;i+=2)state[i]=1;
    res+=check();
    state[1]=0;
    for(int i=2;i<=n;i+=2)state[i]=0;
    for(int i=3;i<=n;i+=2)state[i]=1;
    res+=check();
    return res;
  }
  if(n==1){
    return 1;
  }
  if(n==2){
    if(ban[2-1+3]||ban[1-2+3])return 0;
    return 1;
  }
  now[0][7]=now[1][7]=1;
  for(int i=4;i<=n;i++){
    memset(nxt,0,sizeof nxt);
    for(int j=0;j<2;j++){
      for(int k=0;k<8;k++){
        if(now[j][k]){
          if(j==0){
            solve1(i,k);
          }
          else{
            solve2(i,k);
          }
        }
      }
    }
    memcpy(now,nxt,sizeof nxt);
  }
  for(int i=0;i<2;i++){
    for(int j=0;j<8;j++){
      if(now[i][j]){
        if(i==0){
          if((!(j&1)||!check(n-2,n-1))&&(!(j&2)||!check(n-1,n))&&(!(j&4)||!check(n,n-2))){
            add(res,now[i][j]);
          }
        }
        else{
          if((!(j&1)||!check(n-2,n))&&(!(j&2)||!check(n,n-1))&&(!(j&4)||!check(n-1,n-2))){
            add(res,now[i][j]);
          }
        }
      }
    }
  }
  return res;
}
int main(){
    ios::sync_with_stdio(false);
    cin.tie(0),cout.tie(0);
    cin>>n>>K>>D;
    for(int i=1;i<=K;i++){
      int a,b;cin>>a>>b;
      if(abs(a-b)<=3){
        ban[a][b-a+3]=1;
      }
    }
    cout<<solve();
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值