Codeforces 161C (263C) Circle of Numbers

問題

円周上に1〜nの数が並んでいる。
隣り合う数同士および、二つ隣の数同士が弧で結ばれている。


今、弧がどの数字とどの数字を結んでいるかが与えられる。
このとき、円周上に元の数字がどのような順で並んでいたかを復元せよ。
答えが複数ある場合はどれを出力してもよく、答えが存在しない場合は-1を出力せよ。

制約条件

n≦10^5

方針

全部の数の次数は4としてよい。そうでない場合は-1.
n≧7のとき、
ある数xと弧で結ばれている数をa, b, c, dとする
すると、xの隣にくるのは、a, b, c, dのうち、お互いが弧で結ばれているもの二つ。
(a, cが弧で結ばれているとしたら、a x cと隣り合うはず)


これで、すべての数の隣接関係がわかるので、順番に並べればよい。
n≦6のときは、全探索してよい。


というのが多分模範解答。
答えが作れる場合は一瞬で答えができるので、普通にDFSして、
1.9s以内に答えを作れなかったら、-1を出すという方針で通った。

ソースコード

double getnow(){
	struct timeval tv;
	gettimeofday(&tv, NULL);
	return tv.tv_sec + tv.tv_usec * 1e-6;
}

int n;
set<int> e[100000];
double now;
vi ans;
set<int> u;

void rec(int c){
	if(getnow() - now > 1.95){
		cout << -1 << endl;
		exit(0);
	}
	
	if(ans.size() > 1 && !e[ans[ans.size() - 2]].count(c)) return;
	ans.pb(c);
	u.insert(c);
	
	if(ans.size() == n){
		rep(i, n) cout << ans[i] + 1 << (i==n-1?"\n":" ");
		exit(0);
	}
	each(i, e[c]) if(!u.count(*i)){
		each(j, e[*i]){
			if(!e[c].count(*j)) continue;
			if(ans.size() < n - 1 && u.count(*j)) continue;
			if(ans.size() == n - 1 && *j != ans[0]) continue;
			rec(*i);
		}
	}
	ans.pop_back();
	u.erase(c);
}
int p[100000];
int root(int x){
	if(p[x] < 0) return x;
	return p[x] = root(p[x]);
}

int main(){
	now = getnow();
	memset(p, -1, sizeof(p));
	cin >> n;
	rep(i, 2 * n){
		int a, b;
		cin >> a >> b;
		a--; b--;
		e[a].insert(b);
		e[b].insert(a);
		
		int x = root(a), y = root(b);
		if(x != y){
			p[x] += p[y];
			p[y] = x;
		}
	}
	rep(i, n) if(root(i) != root(0) || e[i].size() != 4) goto FAIL;
	rec(0);
	FAIL:
	cout << -1 << endl;
	return 0;
}