题解 [UNR #5] 获奖名单

传送门

非常巧妙而神奇的绑 subtask 的大细节题

本题的名字长度都只有 1 或 2
所以若形成回文,要么是两个本质相同的名字对位匹配,要么是一些名字形成 A BC D - DC BA 这样的交错匹配
考虑一个巧妙的连边方式:
对于字符集建点,并建出虚点 0
对于长度为 1 的名字,连边 \((0, s_1)\)
对于长度为 2 的名字,连边 \((s_1, s_2)\)
这里均为无向边
那么所有的交错匹配分别对应了从 0 开始的一个回路
这样的话,有解的条件之一就是 0 所在的连通块有欧拉回路
那么从 0 开始跑一个欧拉回路就可以处理掉所有的交错匹配情况
剩下的边就需要形成对位匹配了,那么要求剩下的边每种都出现了偶数次,拿来一一匹配就行了
考虑几种特殊情况:
\(L\) 为偶数时,中间位置可能有一个 11 这样的名字
那么这个东西对应了一个无法形成交错或对位匹配的自环,也就是不在 \(0\) 所在联通块的自环,单独找出来放中间即可
\(L\) 为奇数时,要找的就不是欧拉回路而是欧拉路了,其他部分是不变的
这样的复杂度是 \(O(n)\) 的,但我用了 map

  • 关于找欧拉路的当前弧(?)优化:
    一个容易想到的写法是这样
    for (int i=cur[u]; ~i; cur[u]=i=e[i].next) if (!vis[i>>1]) {
    	vis[i>>1]=1;
    	dfs(e[i].to);
    	sta[++top]=i;
    }
    
    然而这是错误的。具体来说,错在 cur[u]=i=e[i].next
    考虑循环中的 dfs 回溯后,这里会从边 \(i\) 而不是 \(cur_i\) 开始向下枚举
    应该写成这样:
    while (~cur[u]) {
    	int i=cur[u];
    	cur[u]=e[cur[u]].next;
    	if (!vis[i>>1]) {
    		vis[i>>1]=1;
    		dfs(e[i].to);
    		sta[++top]=i;
    	}
    }
    
点击查看代码
#include <bits/stdc++.h>
using namespace std;
#define INF 0x3f3f3f3f
#define N 1000010
#define fir first
#define sec second
#define ll long long
//#define int long long

inline int read() {
	int ans=0, f=1; char c=getchar();
	while (!isdigit(c)) {if (c=='-') f=-f; c=getchar();}
	while (isdigit(c)) {ans=(ans<<3)+(ans<<1)+(c^48); c=getchar();}
	return ans*f;
}

int n, m;
int head[N], ecnt=1;
bool vis[N], vis2[N];
map<pair<int, int>, pair<int, bool>> mp;
int l[N], s[N][3], self[N], sta[N], cur[N], ans1[N], ans2[N], eid[N], p1, p2, len, loop, top;
struct edge{int to, next, id, rev;}e[N<<1];
inline void add(int s, int t, int id, int rev) {e[++ecnt]={t, head[s], id, rev}; head[s]=ecnt;}

int cnt=0;
int cnt2[N];

void dfs(int u) {
	// cout<<"dfs: "<<u<<' '<<cur[u]<<endl;
	// ++cnt;
	// if (clock()>1000000) cout<<"cnt: "<<cnt<<endl;
	vis2[u]=1;
	while (~cur[u]) {
		int i=cur[u];
		cur[u]=e[cur[u]].next;
		if (++cnt2[i>>1] && !vis[i>>1]) {
			vis[i>>1]=1;
			// cout<<"in_edge: "<<i<<' '<<e[i].id<<endl;
			dfs(e[i].to);
			sta[++top]=i;
		}
	}
}

signed main()
{
	n=read(); m=read();
	memset(head, -1, sizeof(head));
	for (int i=1; i<=n; ++i) {
		len+=l[i]=read();
		for (int j=1; j<=l[i]; ++j) s[i][j]=read();
		if (l[i]&1) add(0, s[i][1], i, 0), add(s[i][1], 0, i, 1);
		else {
			add(s[i][1], s[i][2], i, 0), add(s[i][2], s[i][1], i, 1);
			if (s[i][1]==s[i][2]) ++loop, ++self[s[i][1]];
		}
		eid[i]=ecnt>>1;
	}
	p1=0, p2=n+1;
	for (int i=0; i<=m; ++i) cur[i]=head[i];
	if (len&1) {
		dfs(0);
		for (int i=1; i<=n; ++i) if (!vis[i]) {
			pair<int, int> t={s[i][1], s[i][2]};
			pair<int, bool> val={i, t.fir>t.sec};
			if (t.fir>t.sec) swap(t.fir, t.sec);
			if (mp.find(t)!=mp.end()) {
				++p1, ans1[p1]=mp[t].fir, ans2[p1]=mp[t].sec;
				--p2, ans1[p2]=i, ans2[p2]=val.sec^1;
				mp.erase(t);
			}
			else mp[t]=val;
		}
		for (int j=top; j; --j)
			if (j&1) ++p1, ans1[p1]=e[sta[j]].id, ans2[p1]=e[sta[j]].rev;
			else --p2, ans1[p2]=e[sta[j]].id, ans2[p2]=e[sta[j]].rev^1;
	}
	else {
		int loop_id=0;
		dfs(0);
		for (int j=top; j; --j)
			if (j&1) ++p1, ans1[p1]=e[sta[j]].id, ans2[p1]=e[sta[j]].rev;
			else --p2, ans1[p2]=e[sta[j]].id, ans2[p2]=e[sta[j]].rev^1;
		for (int i=1; i<=n; ++i) if (!vis[i] && l[i]==2 && s[i][1]==s[i][2] && self[s[i][1]]&1) {
			vis[eid[i]]=1; loop_id=i;
			break;
		}
		for (int i=1; i<=n; ++i) if (!vis[i]) {
			pair<int, int> t={s[i][1], s[i][2]};
			pair<int, bool> val={i, t.fir>t.sec};
			if (t.fir>t.sec) swap(t.fir, t.sec);
			if (mp.find(t)!=mp.end()) {
				++p1, ans1[p1]=mp[t].fir, ans2[p1]=mp[t].sec;
				--p2, ans1[p2]=i, ans2[p2]=val.sec^1;
				mp.erase(t);
			}
			else mp[t]=val;
		}
		if (loop_id) ++p1, ans1[p1]=loop_id, ans2[p1]=0;
	}
	for (int i=1; i<=n; ++i) printf("%d%c", ans1[i], " \n"[i==n]);
	for (int i=1; i<=n; ++i) printf("%d%c", ans2[i], " \n"[i==n]);
	// cout<<"cnt: "<<cnt<<endl;
	// cout<<"cnt2: "; for (int i=1; i<=n; ++i) cout<<cnt2[i]<<' '; cout<<endl;
	
	return 0;
}
posted @ 2022-05-01 09:38  Administrator-09  阅读(5)  评论(0编辑  收藏  举报