zoukankan      html  css  js  c++  java
  • 【洛谷6896】[ICPC2014 WF] Maze Reduction(哈希)

    点此看题面

    • (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的等价类
    }
    
    败得义无反顾,弱得一无是处
  • 相关阅读:
    牛客练习赛44 A 小y的序列 (模拟,细节)
    牛客假日团队赛10 L 乘积最大 (dp,大数)
    三分查找
    几何基础知识点
    POJ 2318 TOYS
    UVA 11916 Emoogle Grid(大步小步算法(解模方程对数) 快速幂 模的逆)
    UVA 11426 GCD
    Aladdin and the Flying Carpet(算术基本定理)
    算术基本定理
    数论总结帖
  • 原文地址:https://www.cnblogs.com/chenxiaoran666/p/Luogu6896.html
Copyright © 2011-2022 走看看