Codeforces Round #599 (Div. 2) DE题解

D. 0-1 MST

题意:给一个无向图,如果 u , v u,v u,v有边相连,那么他们的边权为1,否则为0,求最小生成树
解法:不难发现最小生成树权值其实可以转化为该无向图的补图的联通块个数减一,那我们就来求补图联通块好了,我们用一个 s e t set set存所有没有用过的点,我们开始任意选择一个 s e t set set里的点开始 b f s bfs bfs,假设出队的点为 u u u,我们枚举 s e t set set的点 v v v,如果 u , v u,v u,v没有边,那么 u , v u,v u,v就是一个联通块,把 v v v加入队列即可,并且从 s e t set set中删除 v v v即可,如果有边,则继续枚举即可
#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e5 + 10;
int n, vis[maxn], cnt[maxn], res;
set<int> s, G[maxn];
queue<int> q;
void bfs(int u)
{
    vis[u] = 1;
    s.erase(u);
    q.push(u);
    while (!q.empty())
    {
        u = q.front();
        q.pop();
        set<int>::iterator it;
        for (it = s.begin(); it != s.end();)
        {
            int v = *it;
            ++it;
            if (G[u].find(v) == G[u].end())
            {
                s.erase(v);
                vis[v] = 1;
                q.push(v);
            }
        }
    }
}
int main()
{
    int m, u, v, ans = 0;
    scanf("%d%d", &n, &m);
    for (int i = 1; i <= n; i++)
        s.insert(i);
    for (int i = 1; i <= m; i++)
    {
        scanf("%d%d", &u, &v);
        G[u].insert(v);
        G[v].insert(u);
    }
    for (int i = 1; i <= n; i++)
        if (!vis[i])
        {
            ans++;
            bfs(i);
        }
    printf("%d\n", ans - 1);
}

E. Sum Balance

题意:有 n n n个数字集合(所有数都不同),你要从每个集合取出一个数字,然后重新分配给每个集合一个数字,使得每个集合的数字和相等,输出选择方案。
解法:我们先求出平均每个集合数字和 s u m sum sum,然后把每个数字看成一个点,枚举每个数字 a i j a_{ij} aij,假设第 i i i个集合的数字和为 s i s_{i} si,如果我把 a i j a_{ij} aij取出去,那么需要补充 v a l = s u m − ( s i − a i j ) val = sum-(s_{i}-a_{ij}) val=sum(siaij),如果 v a l val val存在,且 v a l val val不在 i i i集合或者 v a l = a i j val=a_{ij} val=aij,那么我们可以连一条有向边 a i j − > v a l a_{ij}->val aij>val,问题转化成了 n n n个集合能否组成一个或多个不相交的环,由于 n < = 15 n<=15 n<=15,我们可以先暴力找到所有环包含的集合,然后用状压 d p dp dp去解即可。
#include <bits/stdc++.h>
#define pi pair<int, int>
#define mk make_pair
#define ll long long
using namespace std;
const int maxn = 5001 * 15;
unordered_map<ll, int> mp;
vector<pi> ans[1 << 15];
int cnt, dp[1 << 15];
int k[16], a[16][5001], vis[16], G[maxn];
ll s[16];
pi p[maxn];
void gao(int jie)
{
    memset(vis, 0, sizeof(vis));
    queue<int> q;
    vector<pi> tmp;
    int S = (1 << p[jie].first - 1);
    q.push(jie);
    while (!q.empty())
    {
        int u = q.front();
        q.pop();
        if (u == jie && vis[p[u].first])
        {
            dp[S] = 1;
            ans[S] = tmp;
            return;
        }
        if (vis[p[u].first])
            return;
        vis[p[u].first] = 1;
        if (G[u])
        {
            int v = G[u];
            q.push(v);
            S |= (1 << p[v].first - 1);
            tmp.push_back(mk(a[p[v].first][p[v].second], p[u].first));
        }
    }
}
bool cmp(pi A, pi B)
{
    int p1 = mp[A.first];
    int p2 = mp[B.first];
    return p[p1].first < p[p2].first;
}
int main()
{
    int n;
    scanf("%d", &n);
    ll sum = 0;
    for (int i = 1; i <= n; i++)
    {
        scanf("%d", &k[i]);
        for (int j = 1; j <= k[i]; j++)
        {
            scanf("%d", &a[i][j]);
            sum += a[i][j];
            s[i] += a[i][j];
            mp.emplace(a[i][j], ++cnt);
            p[cnt] = mk(i, j);
        }
    }
    if (sum % n)
        return puts("No"), 0;
    sum /= n;
    for (int i = 1; i <= n; i++)
        for (int j = 1; j <= k[i]; j++)
        {
            ll v = sum - (s[i] - a[i][j]);
            int cur = mp[v];
            if (!cur || (p[cur].first == i && p[cur].second != j))
                continue;
            G[mp[a[i][j]]] = (cur);
        }
    for (int i = 1; i <= cnt; i++)
        gao(i);
    int S = (1 << n) - 1;
    for (int i = 1; i <= S; i++)
        if (!dp[i])
        {
            for (int j = (i - 1) & i; j; j = (j - 1) & i)
                if (dp[j] && dp[i ^ j])
                {
                    dp[i] = 1;
                    ans[i] = ans[j];
                    for (auto tmp : ans[i ^ j])
                        ans[i].push_back(tmp);
                    break;
                }
        }
    if (dp[S] == 0)
        return puts("No"), 0;
    puts("Yes");
    sort(ans[S].begin(), ans[S].end(), cmp);
    for (auto tmp : ans[(1 << n) - 1])
        printf("%d %d\n", tmp.first, tmp.second);
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

长沙橘子猫

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值