题解 [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;
}