【思路要点】
有一种朴素的 O ( N ∗ M ) O(N*M) O(N∗M) 的做法,首先枚举路径开始的位置,那么从这个点开始的每一个点必须只存在一个没有被访问的后继,因此我们可以在 O ( M ) O(M) O(M) 的时间内确定一个起始点出发是否有解,若有解,我们会找到一组唯一的解。
对于有哈密尔顿回路的数据,若我们先找到了一条哈密尔顿回路,那么对于一条非环边,它会使一个区间内的点无法作为起始点,如图中的红点,因此最终可行的起始点一定是环上的一个区间,我们显然能在线性的时间内找到可行的起始点区间并计算答案。
注意到一个起始点出发若不能够找到解,那么从它经过的点出发同样不能够找到解,如果我们将点集 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) :
其实也是存在解决方法的,由于有解的时候存在 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 indi≥2,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 outd≥2 的点称为 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 类点的出边,如下图。
注意到该图中 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]); } }