POI 2001 跳舞蝇

02年的论文题,论文里的方法看不懂QAQ。

然后07年的论文里有一个有向图的同构判定,于是拿来水一水。

结果……


呵呵呵呵呵呵呵呵呵

让我静一静TAT

我觉得肯定是评测机太慢了,我的复杂度是d*n*k的

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
typedef unsigned long long ll;
const int a=566;
const int b=346;
const int c=489;
ll f1[2005][55],f2[2005][55],s[2005];
int p1[2005],p2[2005],n;
void solve(ll f[2005][55],int p[2005],int k){
	for(int i=1;i<=n;i++)s[i]=0;
	for(int i=1;i<=n;i++){
		f[i][k]=f[i][k-1]*a+f[p[i]][k-1]*b;
		s[p[i]]+=f[i][k-1];
	}
	for(int i=1;i<=n;i++)
	f[i][k]+=s[i]*c;
}
int t1[2005],t2[2005];
bool check(){
	for(int i=1;i<=n;i++)
	f1[i][0]=f2[i][0]=1;
	for(int i=1;i<=33;i++){
		solve(f1,p1,i);
		solve(f2,p2,i);
		for(int j=1;j<=n;j++){
			t1[j]=f1[j][i];
			t2[j]=f2[j][i];
		}
		sort(t1+1,t1+1+n);
		sort(t2+1,t2+1+n);
		for(int j=1;j<=n;j++)
		if(t1[j]!=t2[j])return 0;
	}
	return 1;
}
int main(){
	//freopen("a.in","r",stdin);
	int T;scanf("%d",&T);
	while(T--){
		 scanf("%d",&n);
		 for(int i=1;i<=n;i++)scanf("%d",&p1[i]);
		 for(int i=1;i<=n;i++)scanf("%d",&p2[i]); 
		 puts(check()?"T":"N");
	}
	return 0;
}

昨天睡觉的时候突然想起来这个是基环内向树哎。

既然是这样的话我们当然不能用普通的图的hash了。

首先考虑树的部分,当然是有根树的hash了,这个很好搞。

然后设有根树的根在环上。

当然我们知道每条链的尽头都是环(自环也是环)

于是我们想一下环可以怎么hash。

环?循环?字符串的循环同构?(不要问我怎么想到的)

于是我们可以把环看成一个串,串上每个字符就是这个点所连的树的hash值。

然后对每个环求hash值。

最后排序一下比较所有环的hash值(一一对应)


啦啦啦啦啦啦啦啦啦了~

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
#include<queue>
using namespace std;
typedef unsigned long long ll;
const int a=233;
const int b=213;
const int c=417;
const int N=2000+5;
vector<ll>v[N];
ll w[N],xa[N];
int p[N],du[N],n;
void toposort(){
	queue<int>q;
	for(int i=1;i<=n;i++)
	if(!du[i])q.push(i);
	while(!q.empty()){
		int u=q.front();q.pop();
		ll ans=0;
		sort(v[u].begin(),v[u].end());
		for(int i=0;i<v[u].size();i++)
		ans=ans*a+v[u][i];
		w[u]=ans+xa[v[u].size()+1];
		du[p[u]]--;
		v[p[u]].push_back(w[u]);
		if(!du[p[u]])q.push(p[u]);
	}
	for(int i=1;i<=n;i++)
	if(du[i]){
		ll ans=0;
		sort(v[i].begin(),v[i].end());
		for(int j=0;j<v[i].size();j++)
		ans=ans*a+v[i][j];
		w[i]=ans+xa[v[i].size()+1];
		w[i]+=c;
	}
}
void findcircle(vector<ll>& g){
	g.clear();
	for(int i=1;i<=n;i++)
	if(du[i]){
		vector<ll>f;
		int j=i;
		do{
			f.push_back(w[j]);
			j=p[j];
		}while(j!=i);
		sort(f.begin(),f.end());
		ll ans=0;
		for(j=0;j<f.size();j++)
		ans=ans*b+f[j];
		g.push_back(ans);
	}
	sort(g.begin(),g.end());
}
vector<ll>f,g;
void init(){
	for(int i=1;i<=n;i++){
		v[i].clear();
		du[i]=0;
	}
}
int main(){
	//freopen("a.in","r",stdin);
	int T;scanf("%d",&T);
	xa[0]=1;
	for(int i=1;i<=2000;i++)xa[i]=xa[i-1]*a;
	while(T--){
		scanf("%d",&n);
		init();
		for(int i=1;i<=n;i++){
			scanf("%d",&p[i]);
			du[p[i]]++;
		}
		toposort();findcircle(f);
		init();
		for(int i=1;i<=n;i++){
			scanf("%d",&p[i]);
			du[p[i]]++;
		}
		toposort();findcircle(g);
		puts(f==g?"T":"N");
	}
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值