【解析】Hash,离散化。Tarjan

[分析]
对于每一个名字。首先离散化成编号。
用hash或者其它,反正不要最主要的即可了。否则O(N^2L)会爆掉。
然后请參考:http://www.cnblogs.com/Randolph87/p/3757817.html

[吐槽]
难得吐槽一下,做这道题居然做了一个上午,開始暴力求标号,然后一直TLE。
中间map的数据范围少开了N,也试过几次WA。
这不是水题吗。为什么会是一个艰难的Accept...

[小结]
求二分图完备匹配的必要边,①存不存在增广环?

O(N^2) ②直接试着删去依照hungery找完备匹配 O(N^3)。



[代码]

#include <cstdio>
#include <cstring>
#include <cstdlib>
using namespace std;

typedef unsigned long long ULL;
const int N=8002;
const int M=20001;
const int L=12;
const int T=37;
const int LMT=100009;

int n,m;
struct G
{
	int v,nxt;
}map[M+N];
int tt,hd[N];
int dfn[N],pre[N],ct;
int edge[N][2];
int v[N],color[N],stk[N],cc;
char t[L];
struct T
{
	char s[L];
	int nxt;
}hash[N];
int num,hh[LMT];

inline void ins(int u,int v)
{
	map[++tt].v=v;
	map[tt].nxt=hd[u];
	hd[u]=tt;
}

void tarjan(int now)
{
	dfn[now]=pre[now]=++ct;
	stk[++stk[0]]=now,v[now]=1;
	for (int k=hd[now];k;k=map[k].nxt)
	{
		if (v[map[k].v]==2) continue;
		if (v[map[k].v]==1) 
		{
			if (pre[map[k].v]<dfn[now]) dfn[now]=pre[map[k].v];
			continue;
		}
		tarjan(map[k].v);
		if (dfn[map[k].v]<dfn[now]) dfn[now]=dfn[map[k].v];
	}
	if (pre[now]==dfn[now])
	{
		cc++;
		for (;stk[stk[0]]!=now;stk[0]--)
			color[stk[stk[0]]]=cc,v[stk[stk[0]]]=2;
		color[stk[stk[0]]]=cc; v[stk[stk[0]--]]=2;
	}
}

inline ULL h(void)
{
	ULL sum=0; int lt=strlen(t);
	for (int i=0;i<lt;i++) sum=sum*T+t[i]-'0';
	return sum;
}

int pos(void)
{
	int key=h()%LMT;
	for (int k=hh[key];k;k=hash[k].nxt)
		if (!strcmp(hash[k].s,t)) return k;
	memmove(hash[++num].s,t,sizeof t);
	hash[num].nxt=hh[key];
	return hh[key]=num;
}

int main(void)
{	
	int t1,t2;
	scanf("%d",&n);
	for (int i=1;i<=n;i++)
	{
		scanf("%s",t); t1=pos();
		scanf("%s",t); t2=pos();
		edge[i][0]=t1,edge[i][1]=t2;
		ins(t1,t2);
	}
	scanf("%d",&m);
	for (int i=1;i<=m;i++)
	{
		scanf("%s",t); t1=pos();
		scanf("%s",t); t2=pos();
		ins(t2,t1);
	}
	
	for (int i=1;i<=n;i++)
		if (!v[edge[i][0]]) tarjan(edge[i][0]);
	
	for (int i=1;i<=n;i++)
		printf("%s\n",color[edge[i][0]]==color[edge[i][1]]?"Unsafe":"Safe");
	
	return 0;
}