[仙人掌同构 Hash] BZOJ 3899 仙人掌树的同构

本文介绍了一种针对仙人掌图结构的Hash算法,通过将环状结构转换为树形结构进行同构处理,并详细解释了如何处理环上点的顺序问题及实现代码。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Po姐说:http://blog.youkuaiyun.com/PoPoQQQ/article/details/46830025

仙人掌hash

现在变成了仙人掌,那么我把每个环变成一个红点连向环上的所有点,然后把原先环上的边拆除,可以得到一棵树,按树同构做就行了

为了区分红点和普通点的区别,需要为红点设置不同的哈希参数

但是这样有一个BUG,就是原先环上的点是有顺序的,而变成树之后也需要有序

因此对于一个红点,如果它不是树的根节点,计算贡献的时候判断将环翻转是否与原来相同即可 
Hash的时候正反Hash两遍,取最小值


不过他的做法没仔细看诶

看的是这个:http://trinklee.blog.163.com/blog/static/238158060201552051143521


#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<cstring>
#define cl(x) memset(x,0,sizeof(x))
#define P 1000000003LL
using namespace std;
typedef long long ll;
typedef unsigned long long ull;

inline char nc()
{
	static char buf[100000],*p1=buf,*p2=buf;
	if (p1==p2) { p2=(p1=buf)+fread(buf,1,100000,stdin); if (p1==p2) return EOF; }
	return *p1++;
}

inline void read(int &x)
{
	char c=nc(),b=1;
	for (;!(c>='0' && c<='9');c=nc()) if (c=='-') b=-1;
	for (x=0;c>='0' && c<='9';x=x*10+c-'0',c=nc()); x*=b;
}

const int N=1005;
const int M=10005;

int n,m;
ll fac[N],f[N],ans;
ull hash[N],sx[N],num1,loop[N];

struct edge{
	int u,v,next;
};

edge G[M]; int flag[M];
int head[N],inum=1;

inline void add(int u,int v,int p){
	G[p].u=u; G[p].v=v; G[p].next=head[u]; head[u]=p;
}

int fat[N],vst[N];
int cnt,cir[N],size[N];

int first[N],next[N];
inline void cdd(int x,int u){
	size[x]++; cir[u]=x; next[u]=first[x]; first[x]=u;
}

#define V G[p].v
void dfs(int u){
	vst[u]=1;
	for (int p=head[u];p;p=G[p].next)
		if (!flag[p] && !cir[V])
		{
			flag[p]=flag[p^1]=1;
			if (vst[V])
			{
				cdd(++cnt,V);
				for(int i=u;i!=V;i=fat[i]) cdd(cnt,i);
			}
			else
				fat[V]=u,dfs(V);
			flag[p]=flag[p^1]=0;
		}
}

#define nxt(x) ((x)==m?1:(x)+1)
#define pev(x) ((x)==1?m:(x)-1)

void calc(int u,int d){
	f[u]=1; hash[u]=1;
	for (int p=head[u];p;p=G[p].next)
		if (!flag[p] && cir[V]!=cir[u]){
			flag[p]=flag[p^1]=1;
			calc(V,d+1);
			(f[u]*=f[V])%=P;
			flag[p]=flag[p^1]=0;
		}
	int m=0,sm=1,mid;
	if(vst[cir[u]]!=2 && size[cir[u]]!=1){
		vst[cir[u]]=2;
		for (int i=first[cir[u]];i;i=next[i])
			if (i!=u)
	   			calc(i,d+1),(f[u]*=f[i])%=P;
		for (int i=first[cir[u]];i;i=next[i])
		{
			loop[++m]=hash[i];
			if (i==u) mid=m;
		}
		for(int i=pev(mid),j=nxt(mid),t=1;t<=m/2;i=pev(i),j=nxt(j),t++)
			if (loop[i]!=loop[j])
				sm=0;
		if(sm && size[cir[u]]!=2) (f[u]*=2)%=P;
		vst[cir[u]]=0;
	}
	int t=0,j=1;
	for (int p=head[u];p;p=G[p].next)
		if (!flag[p] && cir[u]!=cir[V])
			sx[++t]=hash[V];
	sort(sx+1,sx+t+1);
	for(int i=2;i<=t;i++)
		if(sx[i]==sx[i-1])
			j++;
		else 
			(f[u]*=fac[j])%=P,j=1;
	(f[u]*=fac[j])%=P;
	for (int i=1;i<=m;i++) 
		if (i!=mid)
		{
			int s=abs(i-mid);
			s=min(s,m-s);
			sx[++t]=loop[i]*(233+size[cir[u]])+s;
		}
	sort(sx+1,sx+t+1);
	for (int i=1;i<=t;i++)
		hash[u]=hash[u]*131+sx[i];
	hash[u]*=d;
}

int main()
{
	int iu,iv,icnt=1;
	freopen("t.in","r",stdin);
	freopen("t.out","w",stdout);
	read(n); read(m);
	fac[0]=1; for (int i=1;i<=n;i++) fac[i]=fac[i-1]*i%P;
	for (int i=1;i<=m;i++)
		read(iu),read(iv),add(iu,iv,++inum),add(iv,iu,++inum);
	dfs(1);
	for(int i=1;i<=n;i++) 
		if (!cir[i])
			cir[i]=++cnt,size[cnt]=1;
	calc(1,1);
	ans=f[1]; num1=hash[1];
	for(int i=2;i<=n;i++){
		cl(hash); cl(f);
		calc(i,1);
		if (num1==hash[i]) 
			icnt++;
	}
	printf("%lld\n",ans*icnt%P);
	return 0;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值