- 有(n)个房间,每个房间都是圆形,顺时针给出它连向的每个房间标号。
- 现除去所有标号,若从两个房间出发无法区分彼此,则二者等价,求所有大小超过(1)的等价类。
- (nle100)
恶心的哈希
这题一个非常恶心的地方在于每个房间都是圆形,而边又是按顺时针给出,是存在相对顺序的。
所以说,如果我们沿着一条边走到一个房间,则房间中的边与入边之间的相对顺序是作为一个已知信息存在的。
因此我们记录(f_{i,j,k})表示从(i)出发,选择了第(j)条边,走(k)步的哈希值。
显然从(f_{G_{i,j},p,k-1})转移,(p)要从(j)在(G_{i,j})中对应的编号(w_{G_{i,j},i})开始枚一轮。由于有顺序,每加上一个新的转移值之前要先乘上一个(seed1)。
边界就是(f_{i,j,0}=c_i+1)((c_i)表示(i)的边数)。
搞完之后,我们把每个(f_{i,j,0sim n})哈希一下,这里最好换一个种子(seed2)。
然后由于给定的是一个圆形,我们求出最小表示法,最好再换一个种子(seed3)。
最后只要看哪些房间哈希值相同,就是等价的了。
代码:(O(n^4))
#include<bits/stdc++.h>
#define Tp template<typename Ty>
#define Ts template<typename Ty,typename... Ar>
#define Reg register
#define RI Reg int
#define Con const
#define CI Con int&
#define I inline
#define W while
#define N 100
using namespace std;
int n,c[N+5],w[N+5][N+5],G[N+5][N+5];vector<int> S[N+5];vector<int>::iterator it;
struct Hash
{
#define ull unsigned long long
#define CU Con ull&
ull x,y;I Hash() {x=y=0;}I Hash(CU a):x(a),y(a){}I Hash(CU a,CU b):x(a),y(b){}
I Hash operator + (Con Hash& o) Con {return Hash(x+o.x,y+o.y);}
I Hash operator * (Con Hash& o) Con {return Hash(x*o.x,y*o.y);}
I bool operator < (Con Hash& o) Con {return x^o.x?x<o.x:y<o.y;}
I bool operator == (Con Hash& o) Con {return x==o.x&&y==o.y;}
I friend bool operator <= (Con Hash& A,Con Hash& B) {return A<B||A==B;}
}s1(324682339,456789001),s2(177777777,233333333),s3(71717171,23232323),f[N+5][N+5][N+5],H[N+5];
int cnt;map<Hash,int> id;I int ID(Con Hash& x) {return id.count(x)?id[x]:id[x]=++cnt;}
int main()
{
RI i,j,k;for(scanf("%d",&n),i=1;i<=n;++i) for(scanf("%d",c+i),
j=1;j<=c[i];++j) scanf("%d",G[i]+j),w[i][G[i][j]]=j,f[i][j][0]=c[i];//记录每条边的编号;初始化f[i][j][0]
RI o,p,q;for(k=1;k<=n;++k) for(i=1;i<=n;++i) for(j=1;j<=c[i];++j)//哈希值转移
{
for(p=q=w[o=G[i][j]][i];p<=c[o];++p) f[i][j][k]=f[i][j][k]*s1+f[o][p][k-1];//从当前边开始枚一轮
for(p=1;p^q;++p) f[i][j][k]=f[i][j][k]*s1+f[o][p][k-1];
}
Hash h;for(i=1;i<=n;++i)//求出每个房间的总哈希值
{
if(!c[i]) {S[ID(Hash())].push_back(i);continue;}//特判单点
for(j=1;j<=c[i];++j) for(H[j-1]=Hash(),k=0;k<=n;++k) H[j-1]=H[j-1]*s2+f[i][j][k];//每个f[i][j]先哈希起来
for(o=0,j=1;j^c[i];++j) {for(k=0;k^c[i]&&H[(o+k)%c[i]]==H[(j+k)%c[i]];++k);H[(j+k)%c[i]]<H[(o+k)%c[i]]&&(o=j);}//最小表示法
for(h=Hash(),j=o;j^c[i];++j) h=h*s3+H[j];for(j=0;j^o;++j) h=h*s3+H[j];S[ID(h)].push_back(i);//按最小表示哈希
}
RI tg=0;for(i=1;i<=cnt;++i) if(S[i].size()>1)//如果等价类大小超过1
{for(tg=1,it=S[i].begin();it!=S[i].end();++it) printf("%d ",*it);putchar('
');}//输出等价类中元素
return !tg&&puts("none"),0;//如果没有超过1的等价类
}