IOI2021集训队作业 226AE Maze Reduction
一个图,找出所有等价的点的集合。
等价的点即:两个点\(A,B\)分别开始走,每次可以选一条边走,可以分辨点的度数,可以知道来到一个点的前驱边是谁,能够分辨出连出去的边的顺时针顺序。如果没有办法分辨这两个点,这两个点就是等价的。
\(n\le 100\)
哈希就能过了。。。
设\(f_{k,i,j}\)表示从点\(i\)开始先走第\(j\)条边进行探索,探索了\(k\)步的哈希值。
转移记下点前驱边开始(包括前驱边,如果不包括可能没有地方探索,导致全零的假象)顺时针转,形成的哈希值。
比较点\(x\)和\(y\)时,将\(f_{n,x}\)和\(f_{n,y}\)的最小表示法对比一下。
由于不会最小表示法,直接比较所有循环同构串哈希值的最小值,也能过。
using namespace std;
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <climits>
#define N 105
#define ll long long
#define mo 998244353
#define jd 19260817
ll qpow(ll x,ll y){
ll r=1;
for (;y;y>>=1,x=x*x%mo)
if (y&1)
r=r*x%mo;
return r;
}
int n;
int e[N][N],ne[N];
int re[N][N];
int f[N][N][N];
int vis[N];
int idc[N];
int minidc(int a[],int k){
static int q[N*2];
memcpy(q,a,sizeof(int)*k);
memcpy(q+k,a,sizeof(int)*k);
ll key=0,tmp=qpow(jd,k),res=INT_MAX;
for (int i=0;i<k;++i)
key=(key*jd+q[i])%mo;
for (int i=k;i<k*2;++i){
key=((key*jd+q[i])%mo-tmp*q[i-k]%mo+mo)%mo;
res=min(res,key);
}
return res;
}
bool same(int x,int y){
if (ne[x]!=ne[y]) return 0;
return idc[x]==idc[y];
}
int main(){
freopen("in.txt","r",stdin);
scanf("%d",&n);
for (int i=1;i<=n;++i){
scanf("%d",&ne[i]);
for (int j=0;j<ne[i];++j){
scanf("%d",&e[i][j]);
re[i][e[i][j]]=j;
}
}
for (int i=1;i<=n;++i)
for (int j=0;j<ne[i];++j)
f[0][i][j]=ne[i];
for (int k=1;k<=n;++k)
for (int i=1;i<=n;++i)
for (int j=0;j<ne[i];++j){
int y=e[i][j];
ll key=0;
for (int t=re[y][i];t<ne[y];++t)
key=(key*jd+f[k-1][y][t])%mo;
for (int t=0;t<re[y][i];++t)
key=(key*jd+f[k-1][y][t])%mo;
f[k][i][j]=key;
}
for (int i=1;i<=n;++i)
idc[i]=minidc(f[n][i],ne[i]);
// for (int i=1;i<=n;++i){
// printf("i=%d : %d\n",i,idc[i]);
// for (int j=0;j<ne[i];++j)
// printf("%d ",f[n][i][j]);
// printf("\n");
// }
int bz=0;
for (int i=1;i<=n;++i)
if (!vis[i]){
vis[i]=1;
static int q[N];
int k=1;
q[1]=i;
for (int j=i+1;j<=n;++j)
if (!vis[j] && same(i,j)){
vis[j]=1;
q[++k]=j;
}
if (k>1){
for (int j=1;j<=k;++j)
printf("%d ",q[j]);
printf("\n");
bz=1;
}
}
if (bz==0)
printf("none\n");
return 0;
}