• 【洛谷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的等价类
    }
    
    败得义无反顾,弱得一无是处
  • 相关阅读:
    Spring 中的重试机制,简单、实用!
    Docker 常用命令,还有谁不会?
    Java 对象不使用时为什么要赋值为 null?
    为什么 Nginx 比 Apache 更牛叉?
    你还在用命令看日志?快用 Kibana 吧,一张图片胜过千万行日志!
    golang如何体现面向对象思想
    golang 三⾊标记+GC混合写屏障机制
    Golang中逃逸现象-变量何时 栈何时堆
    golang调度器原理与GMP模型设计思想
    golang 程序内存分析方法
  • 原文地址:https://www.cnblogs.com/chenxiaoran666/p/Luogu6896.html
Copyright © 2020-2023  润新知