首页 > 其他分享 >题解 ABC239F【Construct Highway】

题解 ABC239F【Construct Highway】

时间:2023-05-12 22:13:45浏览次数:148  
标签:连通 ABC239F return int 题解 back nd1 Highway deg

翻译:给定 \(n,m\) 和度数数组 \(\{d_i\}\),再给你 \(m\) 条边,请构造一棵 \(n\) 点的树包含这 \(m\) 条边,且第 \(i\) 个点的度数为 \(d_i\),或者判断无解。


显然,若 \(\sum d_i\ne 2(n-1)\),则无解。

然后对于输入的每条边,使用并查集维护,再求出在这 \(m\) 条边的基础上每个点还需要多少度数(记作 \(d_i\),注意下文 \(d_i\) 的意义与输入不同)。

如果存在 \(d_i < 0\),则无解。

然后考虑每个连通块,维护一个 vector 表示这个连通块中每个点还需要多少度数,若 \(u\) 需要 \(k\) 的度数就存 \(k\) 个 \(u\)。

将所有连通块分为两类:需要度数 \(1\)、需要度数 \(\ge 2\),下文分别称为“第一类”“第二类”。

第一类连通块显然要优先跟第二类连通块连(不然就不连通了)。我们枚举每个第二类连通块,假设这个连通块还需要 \(d\) 的度数,那么拿出 \(d-1\) 的度数连第一类连通块,剩下 \(1\) 的度数会使得这个连通块被归入第一类中。

如果某一时刻第一类连通块不够了,就无解。如果所有第二类连通块都用完了,此时剩下超过 \(2\) 个第一类连通块,就也无解。否则,此时会恰好剩下两个第一类连通块,连起来即可。最后判一下整个图是否连通,不连通还无解。否则输出即可。

这题无解情况挺多的,容易漏情况,写代码之前要想清楚。

//By: OIer rui_er
#include <bits/stdc++.h>
#define rep(x,y,z) for(int x=(y);x<=(z);x++)
#define per(x,y,z) for(int x=(y);x>=(z);x--)
#define debug(format...) fprintf(stderr, format)
#define fileIO(s) do{freopen(s".in","r",stdin);freopen(s".out","w",stdout);}while(false)
using namespace std;
typedef long long ll;

mt19937 rnd(std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::system_clock::now().time_since_epoch()).count());
int randint(int L, int R) {
    uniform_int_distribution<int> dist(L, R);
    return dist(rnd);
}

template<typename T> void chkmin(T& x, T y) {if(x > y) x = y;}
template<typename T> void chkmax(T& x, T y) {if(x < y) x = y;}

const int N = 2e5+5;

int n, m, deg[N];
vector<int> nds[N], nd1;
vector<vector<int>> nd2;
vector<tuple<int, int>> ans;

struct Dsu {
    int fa[N];
    void init(int x) {rep(i, 1, x) fa[i] = i;}
    int find(int x) {return x == fa[x] ? x : fa[x] = find(fa[x]);}
    bool same(int x, int y) {return find(x) == find(y);}
    bool merge(int x, int y) {
        if(same(x, y)) return false;
        x = find(x); y = find(y);
        fa[x] = y;
        return true;
    }
}dsu;

int main() {
    scanf("%d%d", &n, &m);
    rep(i, 1, n) scanf("%d", &deg[i]);
    if(accumulate(deg+1, deg+1+n, 0) != 2 * (n - 1)) return puts("-1")&0;
    dsu.init(n);
    rep(i, 1, m) {
        int u, v;
        scanf("%d%d", &u, &v);
        dsu.merge(u, v);
        --deg[u]; --deg[v];
        if(deg[u] < 0 || deg[v] < 0) return puts("-1")&0;
    }
    rep(i, 1, n) rep(j, 1, deg[i]) nds[dsu.find(i)].push_back(i);
    rep(i, 1, n) {
        if((int)nds[i].size() == 1) nd1.push_back(nds[i][0]);
        if((int)nds[i].size() > 1) nd2.push_back(nds[i]);
    }
    for(auto&& i : nd2) {
        for(int j = 1; j < (int)i.size(); j++) {
            if(nd1.empty()) return puts("-1")&0;
            dsu.merge(i[j], nd1.back());
            ans.emplace_back(i[j], nd1.back());
            nd1.pop_back();
        }
        nd1.push_back(i[0]);
    }
    if((int)nd1.size() > 2) return puts("-1")&0;
    dsu.merge(nd1[0], nd1[1]);
    ans.emplace_back(nd1[0], nd1[1]);
    rep(i, 1, n) if(!dsu.same(i, 1)) return puts("-1")&0;
    for(auto&& [u, v] : ans) printf("%d %d\n", u, v);
    return 0;
}

标签:连通,ABC239F,return,int,题解,back,nd1,Highway,deg
From: https://www.cnblogs.com/ruierqwq/p/abc239f.html

相关文章

  • vi基本入门操作,Ubuntu中vi方向键乱码问题解决方案?
    一、vi基本操作语法:vi+文本名例如创建一个名为text的文本文件进入后先敲击键盘"I"(看个人习惯,敲“a”也是一样的结果,大小写都行),进入插入模式,即可正常输入如果要敲错了内容,和Windows一样,用backspace来删除,也可以用delete键,问题在于用delete键只能删除选中部分的内容,且仅能选......
  • 2023市北区程序设计竞赛题解
    1.分糖果原题: 解题思路:这道题类似于辗转相除法,这道题是辗转相减,每次取余数,如果整除,直接计算答案,并退出,否则继续取余 AC代码:#include<bits/stdc++.h>#definelllonglongusingnamespacestd;intmain(){ lla,b,ans=0;cin>>a>>b; while(a!=b){ if(a<b)swap(a,b......
  • 洛谷 P4544 [USACO10NOV]Buying Feed G - 题解
    https://www.luogu.com.cn/problem/P4544感觉是很没意思的DP+DS优化,以前做过几道更难的题:https://codeforces.com/contest/1788/problem/Ehttps://atcoder.jp/contests/abc288/tasks/abc288_f这种题只要是让我写总是能把代码整的伤痕累累(逃第一眼:我艹不就是一个sbDP吗......
  • 模拟测试题解
    题目链接:https://vjudge.csgrandeur.cn/contest/557480AOJ维护中计算a+b(0<=a,b<=10)。点击查看代码#include<iostream>usingnamespacestd;intmain(){inta,b;cin>>a>>b;cout<<a+b;return0;}B不支持python队列报数,直接模拟即可。点击查看......
  • CF1542E2 题解
    首先,考虑枚举其中一个的逆序对数,这里绕不开的问题就是求\(I_{i,j}\)表示\(1-i\)的排列中逆序对个数为\(j\)的排列数,不妨把这里逆序对变成顺序对(为了方便描述,显然是等价的)。有个很显然的trick:把所有数按\(1-n\)顺序插入。然后当插入第\(i\)个数时,枚举它前面有\(k\)个......
  • 「题解」ABC241Ex Card Deck Score
    小时候最喜欢看的一集没有\(b_i\)怎么做?答案是\([x^m]\prod\frac{1}{1-a_ix}\),我们知道它可以分解为\(\sum\frac{R_i}{1-a_ix}\),其中\(R_i\)是一个常数。具体构造就是上面EI的blog,和中国剩余定理及其类似。那么\(R_i=\frac{1-a_ix}{\prod_j(1-a_jx)}\bmod(1-a_ix)\)......
  • YACS 2022年8月月赛 甲组 T1 约瑟夫问题 题解
    又来填坑了(大雾题目链接#1.为什么用树状数组做多了题目,看一眼这题就知道要用数据结构了,进一步分析就可以知道这是一道二分和树状数组的题目。(其实用变形的链表$n\sqrt{n}$卡卡常也可以吧)#2.具体思路首先设定$n$个位置,第$i$个位置为$1$代表这个人还没出局,否则代表出......
  • agc029c 题解
    首先随便想个暴力,对于\(a_i>a_{i-1}\),我们直接往字符串的末尾加上一些最小的字符。对于\(a_i\lea_{i-1}\),我们保留前缀之后随便加一个位置的\(1\)。发现这个随便的位置不是很好找,于是想到用二分转枚举为判断。二分最大的字符(可以转化为数字)\(x\),每次我们只往最后一......
  • 51nod 1227 平均最小公倍数 题解
    题目大意\[A(n)=\frac{1}{n}\sum_{i=1}^{n}\text{lcm}(n,i)\\F(a,b)=\sum_{i=a}^{b}A(i)\\\]给定\(a,b\),求\(F(a,b)\mod10^9+7\)。\(1\lea\leb\le10^9\)。思路首先我们可以想到,如果我们定义\(B(n)=\underset{i=1}{\overset{n}{\sum}}A(i)\),那么\(F(a,......
  • 【题解】P4331 [BalticOI 2004]Sequence 数字序列
    以各种方式出现被玩烂的题目,算是小trick题?cpeditor意外地好用思路可并堆。平行时空同位体:CF13CP4331P4597CF713CP2893已知做法:\(O(n^2)\)dp:令\(f[i][j]\)为前\(i\)个数不超过\(j\)的最小代价优化:使用堆维护dp产生的折线(P4597题解区)\(O(n\logn......