E. Fair Share (思维,欧拉回路)

73 篇文章 0 订阅

题目

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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值