[Hash] BZOJ 4076 [Wf2014]Maze Reduction

第一眼觉得和一道题莫名相似,实际上并没有什么关系

ft,i,j 表示从第 i 个点,第 j 扇门出发,走 t <script type="math/tex" id="MathJax-Element-261">t</script> 步所有情况的Hash值,那么很好转移

一开始假设掉在房间中间,那我们按照顺序把所有点走过去一遍,取个最小表示,Hash在一起

最后Hash值相同的就是等价了

应该是有不Hash的确定算法的?谁知道呢

#include<cstdio>
#include<cstdlib>
#include<vector>
#include<algorithm>
#define pb push_back
using namespace std;
typedef unsigned long long ull;

#define read(x) scanf("%d",&(x))

const int N=105;

int n;
int G[N][N],back[N][N];
ull H[N][N][N];

const ull ORI1=2333,ORI2=23333;
const ull BASE1=999911657,BASE2=999911659;
const ull END1=18357,END2=81643;

ull HH[N];
bool cmp(int x,int y){
  return HH[x]==HH[y]?x<y:HH[x]<HH[y];
}
ull lst[N],pnt;
int idx[N];

inline int mpa(ull *a,int n){  
  int i = 0, j = 1, k = 0;  
  while(i < n && j < n && k < n){  
    ull x=a[(i + k) % n],y=a[(j + k) % n];  
    if(x==y) k ++;  
    else{  
      if(x>y) i += k + 1;  
      else j += k + 1;  
      if(i == j) i ++;   
    }   
  }  
  return min(i, j);   
}

int cnt;
int K[N][N];

inline bool cmp2(int x,int y){
  return K[x][1]<K[y][1];
}

int main(){
  freopen("t.in","r",stdin);
  freopen("t.out","w",stdout);
  read(n);
  for (int i=1;i<=n;i++){
    read(*G[i]); for (int j=1;j<=*G[i];j++) read(G[i][j]),back[i][G[i][j]]=j;
  }
  for (int i=1;i<=n;i++)
    for (int j=1;j<=*G[i];j++)
      H[0][i][j]=(ull)(*G[i]);
  for (int t=1;t<=n;t++)
    for (int i=1;i<=n;i++)
      for (int j=1;j<=*G[i];j++){
    H[t][i][j]=ORI1;
    for (int k=j,c=1;c<=*G[i];k=k%*G[i]+1,c++){
      int v=G[i][k]; ull h=H[t-1][v][back[v][i]];
      (((H[t][i][j]*=BASE1)+=h)^=h)+=h;
    }
    ((H[t][i][j]+=END1)*=END1)^=END1;
      }
  for (int i=1;i<=n;i++){
    pnt=0;
    for (int j=1;j<=*G[i];j++){
      ull hh=ORI2,h; int v=G[i][j];
      for (int t=0;t<=n;t++)
    h=H[t][v][back[v][i]],(((hh*=BASE2)+=h)^=h)+=h;
      ((hh+=END2)*=END2)^=END2;
      lst[++pnt]=hh;
    }
    int x=mpa(lst+1,pnt)+1;
    HH[i]=ORI2;
    for (int j=x,c=1;c<=pnt;j=j%pnt+1,c++)
      (((HH[i]*=BASE2)+=lst[j])^=lst[j])+=lst[j];
    ((HH[i]+=END2)*=END2)^=END2;
  }
  for (int i=1;i<=n;i++) idx[i]=i;
  sort(idx+1,idx+n+1,cmp); int flag=0;
  for (int i=1,j;i<=n;i=j+1){
    j=i; while (j+1<=n && HH[idx[i]]==HH[idx[j+1]]) j++;
    if (j-i+1>1){
      flag=1; ++cnt;
      for (int k=i;k<=j;k++)
    K[cnt][++*K[cnt]]=idx[k];
    }
  }
  if (!flag)
    printf("none\n");
  else{
    for (int i=1;i<=cnt;i++) idx[i]=i;
    sort(idx+1,idx+cnt+1,cmp2);
    for (int i=1;i<=cnt;i++){
      int x=idx[i];
      for (int j=1;j<=*K[x];j++)
    printf("%d%c",K[x][j],j==*K[x]?'\n':' ');
    }
  }
  return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值