【省内训练2018-09-13】Hamilton Path

【思路要点】

  • 有一种朴素的 O ( N ∗ M ) O(N*M) O(NM) 的做法,首先枚举路径开始的位置,那么从这个点开始的每一个点必须只存在一个没有被访问的后继,因此我们可以在 O ( M ) O(M) O(M) 的时间内确定一个起始点出发是否有解,若有解,我们会找到一组唯一的解。

  • 对于有哈密尔顿回路的数据,若我们先找到了一条哈密尔顿回路,那么对于一条非环边,它会使一个区间内的点无法作为起始点,如图中的红点,因此最终可行的起始点一定是环上的一个区间,我们显然能在线性的时间内找到可行的起始点区间并计算答案。

    【省内训练2018-09-13】Hamilton Path_第1张图片

  • 注意到一个起始点出发若不能够找到解,那么从它经过的点出发同样不能够找到解,如果我们将点集 r a n d o m _ s h u f f l e random\_shuffle random_shuffle 一下,每次选取起始点都会期望删除一半的错误起始点,因此期望 O ( L o g N ) O(LogN) O(LogN) 次选取起始点后,我们能够找到哈密尔顿回路或者没有起始点可以选择,期望时间复杂度 O ( M L o g N ) O(MLogN) O(MLogN)

  • 在比赛时笔者发现加上这个优化后程序获得了 A C AC AC (下文代码就是这个做法)。

  • 事实上,当不存在哈密尔顿回路,但问题有解的情况下,问题的解的数量不超过 2 2 2 (下文将给予说明)。这个算法同样能够在 O ( M L o g N ) O(MLogN) O(MLogN) 的期望时间复杂度内给出结果。但这个算法在不存在哈密尔顿回路,且问题无解的情况下的时间复杂度可能退化,考虑如下数据,该算法将退化至 O ( N 2 ) O(N^2) O(N2)

    【省内训练2018-09-13】Hamilton Path_第2张图片

  • 其实也是存在解决方法的,由于有解的时候存在 O ( M L o g N ) O(MLogN) O(MLogN) 的期望时间复杂度,所以若一个数据跑了很久没有出解,那这个数据的答案就是无解。总时间复杂度 O ( M L o g N ) O(MLogN) O(MLogN)

  • 以上是笔者在考场上的乱搞做法,下面我们来讲这个题的正解。

  • 首先,若存在点 i i i ,满足 i n d i ≥ 2 , o u t d i = 1 ind_i≥2,outd_i=1 indi2,outdi=1 ,其中 i n d i ind_i indi 表示 i i i 的入度, o u t d i outd_i outdi 表示 i i i 的出度,那么 i i i 的后继结点必然是 i i i 唯一的出点,我们把 i i i i i i 的出点缩在一起考虑。

  • 注意到此时若图中存在哈密尔顿回路,那么缩点后的图去掉自环后将是一个环,或者出现双向边(此时问题无解),接下来我们认为图中不存在哈密尔顿回路。

  • 此时,剩余的点中,若存在 i n d i = 0 ind_i=0 indi=0 的点,那么它一定是起始点,若有多个这样的点问题显然无解。

  • 我们将剩余的点分为三类: i n d = o u t d = 1 ind=outd=1 ind=outd=1 的点称为 A A A 类点, o u t d ≥ 2 outd≥2 outd2 的点称为 B B B 类点, o u t d = 0 outd=0 outd=0 的点称为 C C C 类点, C C C 类点显然只能有一个。

  • 只有 A A A 类点可能成为起始点,其余点由于出度不对,无法成为起始点。

  • 一个能够作为起始点的 A A A 类点应当满足沿着它的出边走到的第一个点非 A A A 类点为 B B B 类点,且该 B B B 类点存在一条指向该 A A A 类点的出边,如下图。

    【省内训练2018-09-13】Hamilton Path_第3张图片

  • 注意到该图中 A A A 点没有其他的入边,也就是说,若 A A A 点不是起始点,这样的结构一旦进入,就无法走出去了,因此这样的结构在图中至多存在两个,否则问题无解。

  • 那么,我们只需要暴力检验这两个起始点是否能够得到一组解即可。

  • 时间复杂度 O ( N + M ) O(N+M) O(N+M)

【代码】

#include
using namespace std;
const int MAXN = 5e5 + 5;
const int MAXM = 1e6 + 5;
const int P = 1e9 + 7;
template <typename T> void read(T &x) {
	x = 0; int f = 1;
	char ch = getchar();
	for (; !isdigit(ch); ch = getchar()) if (ch == '-') f = -f;
	for (; isdigit(ch); ch = getchar()) x = x * 10 + ch - '0';
	x *= f;
}
template <typename T> void write(T x) {
	if (x < 0) x = -x, putchar('-');
	if (x > 9) write(x / 10);
	putchar(x % 10 + '0');
}
template <typename T> void writeln(T x) {
	write(x);
	puts("");
}
bool solved, vis[MAXN], ban[MAXN];
int n, m, bit[MAXN];
int x[MAXM], y[MAXM];
int p[MAXN], home[MAXN];
int cnt, ans[MAXN];
vector <int> a[MAXN];
void check(int pos, int sum) {
	bool loop = false;
	for (unsigned i = 0; i < a[pos].size(); i++)
		if (a[pos][i] == p[1]) loop = true;
	if (!loop) {
		ans[++cnt] = sum;
		return;
	}
	solved = true;
	static int d[MAXN * 2];
	for (int i = 1; i <= 2 * n; i++)
		d[i] = 0;
	cnt = 0;
	for (int i = 1; i <= n; i++) {
		ans[i] = -1;
		home[p[i]] = i;
	}
	for (int i = 1; i <= m; i++) {
		int tx = home[x[i]], ty = home[y[i]];
		if (tx + 1 == ty || (tx == n && ty == 1)) continue;
		if (tx < ty) tx += n;
		d[ty + 1]++, d[tx + 1]--;
	}
	for (int i = 1; i <= 2 * n; i++)
		d[i] += d[i - 1];
	for (int i = 1; i <= n; i++) {
		if (d[i] == 0 && d[i + n] == 0) {
			cnt++;
			ans[p[i]] = sum;
		}
		sum = (sum - 1ll * bit[n - 1] * p[i] % P + P) % P;
		sum = (sum * 10ll + p[i]) % P;
	}
	writeln(cnt);
	for (int i = 1; i <= n; i++)
		if (ans[i] != -1) {
			write(ans[i]);
			putchar(' ');
		}
	printf("\n");
	return;
}
void work(int pos, int sum) {
	for (int i = 1; i <= n; i++) {
		sum = (sum + 1ll * bit[n - i] * pos) % P;
		p[i] = pos;
		if (i == n) {
			check(pos, sum);
			for (int j = 1; j <= n; j++)
				vis[j] = false;
			return;
		}
		vis[pos] = true;
		int dest = 0;
		for (unsigned j = 0; j < a[pos].size(); j++)
			if (!vis[a[pos][j]]) {
				if (dest == 0) dest = a[pos][j];
				else {
					for (int k = 1; k <= i; k++) {
						vis[p[k]] = false;
						ban[p[k]] = true;
					}
					return;
				}
			}
		if (!dest) {
			for (int k = 1; k <= i; k++) {
				vis[p[k]] = false;
				ban[p[k]] = true;
			}
			return;
		}
		pos = dest;
	}
}
int main() {
	int T; read(T);
	while (T--) {
		read(n), read(m);
		bit[0] = 1;
		for (int i = 1; i <= n; i++) {
			a[i].clear(), ban[i] = false;
			bit[i] = bit[i - 1] * 10ll % P;
		}
		for (int i = 1; i <= m; i++) {
			read(x[i]), read(y[i]);
			a[x[i]].push_back(y[i]);
		}
		for (int i = 1; i <= n; i++) {
			sort(a[i].begin(), a[i].end());
			a[i].resize(unique(a[i].begin(), a[i].end()) - a[i].begin());
		}
		cnt = 0, solved = false;
		for (int i = 1; i <= n; i++) {
			if (!ban[i]) work(i, 0);
			if (solved) break;
		}
		if (!solved) {
			printf("%d\n", cnt);
			if (cnt >= 1 && cnt <= n) {
				for (int i = 1; i <= cnt; i++)
					printf("%d ", ans[i]);
				printf("\n");
			}
		}
	}
	return 0;
}

【标程】

#include 
using namespace std;
#define rep(i,a,n) for (int i=a;i
#define per(i,a,n) for (int i=n-1;i>=a;i--)
#define pb push_back
#define mp make_pair
#define all(x) (x).begin(),(x).end()
#define fi first
#define se second
#define SZ(x) ((int)(x).size())
typedef vector<int> VI;
typedef long long ll;
typedef pair<int,int> PII;
const ll mod=1000000007;
ll powmod(ll a,ll b) {ll res=1;a%=mod; assert(b>=0); for(;b;b>>=1){if(b&1)res=res*a%mod;a=a*a%mod;}return res;}
ll gcd(ll a,ll b) { return b?gcd(b,a%b):a;}
// head

const int N=501000;
VI e[N],r[N];
int he[N];
int n,m,u,v,vis[N],mt,ret[N],_;
int f[N],pre[N],nxt[N],t[N];
int ind[N],oud[N],cnt[N];
int d[N];
bool fix[N];
PII q[N];
VI pt[3];

int find(int x) { return f[x]==x?x:f[x]=find(f[x]); }

PII E[N*2];

struct Hash_table {
	static const int V=10000003;
	int fst[V],nxt[V];
	int ctm,ptm[V],T;
	ll key[V];
	void init() { T=0; ctm++;}
	bool insert(ll s) {
		int S=s%V;
		if (ptm[S]!=ctm) ptm[S]=ctm,fst[S]=-1;
		for (int j=fst[S];j!=-1;j=nxt[j]) if (key[j]==s) {
			return 1;
		}
		nxt[T]=fst[S],fst[S]=T,key[T]=s;
		T++;
		return 0;
	}
	bool query(ll s) {
		int S=s%V;
		if (ptm[S]!=ctm) return 0;
		for (int j=fst[S];j!=-1;j=nxt[j]) if (key[j]==s) 
			return 1;
		return 0;
	}
}hs;


void init() {
	scanf("%d%d",&n,&m);
	rep(i,0,m) {
		scanf("%d%d",&u,&v);
		E[i]=mp(u,v);
	}
}

bool check(int u,int ty) {
	mt++;
	rep(i,0,n) {
		vis[u]=mt;
		d[i]=u;
		if (i==n-1) break;
		VI &c=ty?r[u]:e[u];
		int nxt=0; int ns=0;
		for (auto v:c) if (vis[v]!=mt) {
			ns++; nxt=v;
		}
		if (ns>1||ns==0) return 0;
		u=nxt;
	}
	return 1;
}

int gao() {
	rep(i,1,n+1) {
		ind[i]=SZ(r[i]);
		oud[i]=SZ(e[i]);
		f[i]=i;
		t[i]=i;
		nxt[i]=0;
		pre[i]=0;
	}
	int tt=0;
	rep(i,1,n+1) if (ind[i]>=2&&oud[i]==1) {
		nxt[i]=he[i];
		q[tt++]=mp(i,nxt[i]);
	}
	rep(i,0,tt) {
		int u=q[i].fi;
		int v=q[i].se;
		if (he[u]==0) return -1;
		for (auto p:r[v]) {
			if (find(v)==find(p)) continue;
			he[p]-=v;
			--oud[p];
			if (ind[find(p)]>=2&&oud[p]==1&&nxt[p]==0) {
				nxt[p]=he[p];
				q[tt++]=mp(p,nxt[p]);
			}
		}
		int p=find(u);
		t[p]=t[find(v)];
		f[find(v)]=p;
		if (hs.query(1ll*t[p]*n+p)) {
			he[t[p]]-=p;
			--oud[t[p]];
			--ind[p];
		}
		if (ind[p]>=2&&oud[t[p]]==1&&nxt[t[p]]==0) {
			nxt[t[p]]=he[t[p]];
			q[tt++]=mp(t[p],nxt[t[p]]);
		}
	}

	int rt=0;
	int srt=0;
	rep(i,1,n+1) if (find(i)==i&&ind[i]==0) {
		rt=i; srt++;
	}
	if (srt>=2) return -1;
	if (srt==1) {
		if (check(rt,0)) {
			pt[0]=VI(d,d+n);
			return 1;
		}
		return -1;
	}
	rt=srt=0;
	rep(i,1,n+1) if (t[find(i)]==i&&oud[i]==0) {
		rt=i; srt++;
	}
	if (srt>=2) return -1;
	if (srt==1) {
		if (check(rt,1)) {
			pt[0]=VI(d,d+n);
			reverse(all(pt[0]));
			return 1;
		}
		return -1;
	}

	bool cyc=1;
	rep(i,1,n+1) if (find(i)==i&&(ind[i]!=1||oud[t[i]]!=1)) {
		cyc=0;
		break;
	}
	rep(i,1,n+1) if (find(i)==i&&oud[t[i]]==1) {
		nxt[t[i]]=he[t[i]];
	}

	rep(u,1,n+1) if (find(u)==u&&ind[u]==1) {
		int ns=0,nc=-1;
		for (auto v:r[u]) if (t[find(v)]==v&&find(v)!=find(u)) ns++,nc=v;
		pre[u]=nc;
	}
	if (cyc) {
		mt++;
		int rt=1;
		for (int i=0;i<n;i++) {
			if (vis[rt]==mt) return -1;
			vis[rt]=mt;
			d[i]=rt;
			ind[rt]=i;
			rt=nxt[rt];
		}
		pt[0]=VI(d,d+n);
		return 0;
	}
	rt=-1;
	rep(i,1,n+1) if (find(i)==i&&ind[i]==1&&oud[t[i]]==1) rt=i;
	if (rt==-1) return -1;
	mt++;
	int tot=0;
	rep(u,1,n+1) if (find(u)==u&&vis[u]!=mt&&pre[u]!=0) {
		int v=find(pre[u]);
		if (ind[v]>=2) {
			int w=u;
			while (1) {
				vis[w]=mt;
				int x=nxt[w];
				if (find(x)==x&&oud[t[x]]>=2) {
					if (x==v) {
						if (check(w,1)) {
							reverse(d,d+n);
							pt[tot++]=VI(d,d+n);
						}
						if (check(u,0)) {
							pt[tot++]=VI(d,d+n);
						}
						if (tot==0) return -1;
						return tot;
					}
					break;
				}
				if (x==0||x==u) break;
				w=x;
			}
		}
	} 
	return -1;
}

vector<int> solve() {
	rep(i,1,n+1) {
		e[i].clear();
		r[i].clear();
		he[i]=0;
		f[i]=i;
	}
	hs.init();
	rep(i,0,m) {
		u=E[i].fi;
		v=E[i].se;
		if (hs.insert(1ll*u*n+v)) continue;
		he[u]+=v;
		e[u].pb(v);
		r[v].pb(u);
	}

	int c=gao();
	if (c==-1) {
		return VI(0);
	} else if (c==0) {
		VI ans;
		int nc=0;
		rep(i,0,n) cnt[i]=0;
		rep(i,0,m) {
			u=ind[E[i].fi],v=ind[E[i].se];
			if ((u+1)%n==v) continue; 
			u=(u+1)%n;
			nc++;
			if (u<v) {
				cnt[u]++;
				cnt[v+1]--;
			} else {
				cnt[u]++;
				cnt[0]++;
				cnt[v+1]--;

			}
		}
		int s=0;
		rep(i,1,n+1) ind[i]=0;
		ll hv=0,pw=1;
		rep(i,0,n) {
			hv=(hv*10+pt[0][i])%mod;
			pw=pw*10%mod;
		}
		rep(i,0,n) {
			s+=cnt[i];
			ret[pt[0][i]]=hv;
			if (s==nc) ind[pt[0][i]]=1;
			hv=(hv*10+(mod+1-pw)*pt[0][i])%mod;
		}
		rep(i,1,n+1) if (ind[i]==1) ans.pb(ret[i]);
		return ans;
	} else {
		sort(pt,pt+c);
		VI ans;
		rep(i,0,c) {
			ll hv=0;
			rep(j,0,n) hv=(hv*10+pt[i][j])%mod;
			ans.pb(hv);
		}
		return ans;
	}
}

int main() {
	for (scanf("%d",&_);_;_--) {
		init();
		VI ans=solve();
		printf("%d\n",SZ(ans));
		rep(i,0,SZ(ans)) printf("%d%c",ans[i]," \n"[i==SZ(ans)-1]);
	}
}

你可能感兴趣的:(【类型】做题记录,【算法】构造与证明,【算法】拓扑排序)