题目
https://codeforces.com/contest/1634/problem/E
题意大概:给出 m 个偶数长度的数字串,要你把全部数字分到两个集合(L, R)中,使得每个串中一半数字在L,另一半在 R,且最终两个集合成分相同。若可以构造,要给出一种构造;若无法构造,则输出NO。
思路
https://codeforces.com/blog/entry/99563
看了题解做的。题解很巧妙地把这个问题转化为了图论问题,求欧拉环。
先看看怎样会无解:若所有数列算在一起,某个数字只出现了奇数次,那么肯定无解。这是不是无解的充要条件呢?看看下面的构造就发现,确实是充要条件,只要满足每个数字都出现了偶数次,那么肯定可以构造出解。
在“数列长度全为偶数,数字出现次数全为偶数”的前提下:
这样建无向图:共(m+sigma(n))个点,其中有m个点代表数字串,其他点代表这些数列中出现过的每种数字。图的每条边对应对数列中的一位,第 i 个数列的第 j 位为数字 k ,则图中就会有一条连接 “数列 i” 与 “数字 k” 的边。 这个图也是个二分图。
欧拉回路的存在性:因为图中每个点的度数都是偶数,所以欧拉回路存在。
这样一来,找到一条(或多条,取决于图的连通性)欧拉回路(不重复地走完所有边),按路径奇偶分配就行了。
求欧拉回路,用dfs搜,每次搜到没路了(考虑度数奇偶性,思考一下就发现这时肯定是回到起点),就再以中间还有路的点开始搜,走完一些边后肯定又回到那个点……做好对点和边的记忆化,就可以线性时间复杂度求出欧拉回路了。
代码
#include <bits/stdc++.h>
using namespace std;
const int MAXN = 200005;
vector<pair<int, int>> graph[MAXN*2];
vector<int> ans[MAXN];
map<int, int> M;
int sid[MAXN*2]; // 用于记录枚举到以这个点为起点的哪条边了
void dfs(int X, int flag)
{
int u=X;
while (sid[u]) {
sid[u]--;
int v=graph[u][sid[u]].first;
int pos=graph[u][sid[u]].second;
if (ans[min(u, v)][pos]<0) {
ans[min(u, v)][pos]=flag;
dfs(v, flag^1);
}
}
}
void solve()
{
int mxid=MAXN, m;
scanf("%d",&m);
for (int i=1, len, v; i<=m; i++) {
scanf("%d",&len);
ans[i].resize(len+1, -1);
for (int j=1; j<=len; j++) {
scanf("%d",&v);
if (M[v]==0)
v=M[v]=++mxid;
else
v=M[v];
graph[i].push_back(make_pair(v, j));
graph[v].push_back(make_pair(i, j));
sid[i]++;
sid[v]++;
}
}
for (int i=MAXN*2; i>MAXN; i--) {
if (sid[i]&1) {
puts("NO");
return;
}
}
for (int i=1; i<=mxid; i++) {
dfs(i, 0);
}
puts("YES");
for (int i=1; i<=m; i++) {
for (int j=1; j<ans[i].size(); j++)
putchar(ans[i][j] ? 'L' : 'R');
puts("");
}
}
int main()
{
solve();
return 0;
}