@codeforces - 141E@ Clearing Up
@description@
给定一个 N 个点 M 条边的图,每条为黑色或者白色。
现在让你求一个生成树,使得生成树中黑色边数量等于白色边数量。
Input
第一行两个整数 n, m (1 ≤ n ≤ 10^3, 1 ≤ m ≤ 10^5)。
接下来 m 行,每行描述了一条边 x, y,并给定它的颜色 —— S 为白色,M 为黑色。
注意可以有重边自环。
Output
如果无解,输出 -1。
否则,先输出边的数量,再在下一行依次给出每条边的编号(根据输入顺序)。
如果有多解,输出任意一个解。
Examples
Input
3 3
1 2 S
1 3 M
2 3 S
Output
2
2 1
@solution@
考虑黑色边中有一些必要边:即使加入所有的白边,也需要这些必要边才能保证连通。
白色边,类似地也有必要边的定义:加入所有的黑边也需要这些边才能连通。
可以通过并查集 + 先扫描白色边 + 再尝试加入黑色边求出黑色必要边。同理可以求出白色必要边。
不妨考虑一种情况:即所有边都不是必要边。
这种情况下,黑色边可以让整张图保持连通,白色边也可以让整张图保持连通。
那么我只需要任意加入 (n - 1) / 2 条不形成环的白边,一定可以另外选出 (n - 1) / 2 条黑边形成生成树。
至于为什么。将白边连接的连通块缩成一个新点,则新图中黑边依然可以让整张图保持连通,而连通图一定可以求出一个生成树。
实现直接并查集加 (n - 1) / 2 条白边,再尽量多地塞黑边。
假如我把必要边连接的连通块缩成点,此时所有边都不为必要边,则归于上面那一种情况。
注意特殊情况,如 n - 1 为奇数,或整张图根本不连通,或必要边太多等。
@accepted code@
#include<cstdio>
#include<vector>
#include<algorithm>
using namespace std;
const int MAXN = 100000;
int x[MAXN + 5], y[MAXN + 5];
vector<int>v[2], ans;
bool tag[2][MAXN + 5];
int fa[MAXN + 5], n, m;
int find(int x) {
return fa[x] = (fa[x] == x ? x : find(fa[x]));
}
bool unite(int x, int y) {
int fx = find(x), fy = find(y);
if( fx != fy ) {
fa[fx] = fy;
return true;
}
else return false;
}
int tot[2];
void get(int t) {
for(int i=1;i<=n;i++) fa[i] = i;
for(int i=0;i<v[!t].size();i++)
unite(x[v[!t][i]], y[v[!t][i]]);
for(int i=0;i<v[t].size();i++)
if( unite(x[v[t][i]], y[v[t][i]]) )
ans.push_back(v[t][i]), tot[t]++, tag[t][i] = true;
}
int main() {
scanf("%d%d", &n, &m);
for(int i=1;i<=m;i++) {
scanf("%d%d", &x[i], &y[i]);
char ch = getchar();
while( ch != 'S' && ch != 'M' )
ch = getchar();
if( ch == 'S' ) v[0].push_back(i);
if( ch == 'M' ) v[1].push_back(i);
}
if( (n - 1) & 1 ) {
puts("-1");
return 0;
}
int mid = (n - 1) >> 1;
get(0), get(1);
if( tot[0] > mid || tot[1] > mid ) {
puts("-1");
return 0;
}
for(int i=1;i<=n;i++) fa[i] = i;
for(int i=0;i<ans.size();i++) unite(x[ans[i]], y[ans[i]]);
for(int i=0;i<v[0].size()&&tot[0]<mid;i++)
if( !tag[0][i] && unite(x[v[0][i]], y[v[0][i]]) )
ans.push_back(v[0][i]), tot[0]++, tag[0][i] = true;
for(int i=0;i<v[1].size()&&tot[1]<mid;i++)
if( !tag[1][i] && unite(x[v[1][i]], y[v[1][i]]) )
ans.push_back(v[1][i]), tot[1]++, tag[1][i] = true;
if( tot[0] != mid || tot[1] != mid ) {
puts("-1");
return 0;
}
printf("%d\n", ans.size());
for(int i=0;i<ans.size();i++)
printf("%d ", ans[i]);
}
@details@
定睛一看。嗯?wqs二分?
不对wqs二分只能求最小权和不能求方案。
注意各种无解的情况吧。其他没有什么细节。
如果一句话总结的话,大概就是:任意一个连通块必然有生成树。
@codeforces - 141E@ Clearing Up的更多相关文章
- CodeForces 141E: ...(最小生成树)
[条件转换] 两两之间有且只有一条简单路径<==>树 题意:一个图中有两种边,求一棵生成树,使得这棵树中的两种边数量相等. 思路: 可以证明,当边的权是0或1时,可以生成最小生成树到最大生 ...
- python爬虫学习(5) —— 扒一下codeforces题面
上一次我们拿学校的URP做了个小小的demo.... 其实我们还可以把每个学生的证件照爬下来做成一个证件照校花校草评比 另外也可以写一个物理实验自动选课... 但是出于多种原因,,还是绕开这些敏感话题 ...
- 【Codeforces 738D】Sea Battle(贪心)
http://codeforces.com/contest/738/problem/D Galya is playing one-dimensional Sea Battle on a 1 × n g ...
- 【Codeforces 738C】Road to Cinema
http://codeforces.com/contest/738/problem/C Vasya is currently at a car rental service, and he wants ...
- 【Codeforces 738A】Interview with Oleg
http://codeforces.com/contest/738/problem/A Polycarp has interviewed Oleg and has written the interv ...
- CodeForces - 662A Gambling Nim
http://codeforces.com/problemset/problem/662/A 题目大意: 给定n(n <= 500000)张卡片,每张卡片的两个面都写有数字,每个面都有0.5的概 ...
- CodeForces - 274B Zero Tree
http://codeforces.com/problemset/problem/274/B 题目大意: 给定你一颗树,每个点上有权值. 现在你每次取出这颗树的一颗子树(即点集和边集均是原图的子集的连 ...
- CodeForces - 261B Maxim and Restaurant
http://codeforces.com/problemset/problem/261/B 题目大意:给定n个数a1-an(n<=50,ai<=50),随机打乱后,记Si=a1+a2+a ...
- CodeForces - 696B Puzzles
http://codeforces.com/problemset/problem/696/B 题目大意: 这是一颗有n个点的树,你从根开始游走,每当你第一次到达一个点时,把这个点的权记为(你已经到过不 ...
随机推荐
- 【大数据】Hadoop常用启动命令
Hadoop常用启停命令 最近在装大数据环境,不知由于年纪大的问题还是笨的缘故,老师记不住一些常用命令,在这里就单独记一下Hadoop常用的启停命令.Hadoop常用的启停命令都在hadoop/sbi ...
- IDEA设置谷歌浏览器和火狐浏览器打开
我们在软件的功能栏中选择file,然后使用鼠标点击settings,进入到软件的详细设置界面 进入后,我们可以看到很多设置项,然后我们使用鼠标依次点击左侧的tools和web browsers,进 ...
- jnhs解决办法部署错误: 未能启动 Tomcat, 服务器端口 8084 已在使用中。
当然重启电脑是不可能重启电脑的,这辈子都不会重启电脑 解决方法 1. win + R,输入cmd回车进打开命令行工具 2. 输入 netstat -ano|findstr 8084 查看占用8080端 ...
- python 内置操作函数
- 开发者必看!探秘阿里云Hi购季开发者分会场:海量学习资源0元起!
摘要: 开发者分会场致力于帮助开发者学习了解阿里云最新技术,为开发者设计全方位的技术成长与进阶之路. 2019阿里云云上Hi购季活动已经于2月25日正式开启,从已开放的活动页面来看,活动分为三个阶段: ...
- 【51nod1563】坐标轴上的最大团 贪心
题面 坐标轴上有n个点,每个点有一个权值.第i个点的坐标是 xi ,权值是 wi .现在对这些点建图.对于点对 (i,j) ,如果 \(|xi−xj|≥wi+wj\) ,那么就给第i个点和第j个点之间 ...
- qt加载背景图片的一种方式
//加载背景图片 void LCTGrid::loadBgPicture() { QImage image; QPalette palette; image.load(m_sPicturePath); ...
- c++进制数转换
QString result; ]; sprintf(buffer, "0x%x", modelId); return result = buffer;
- 微信小程序分析见解
前两天朋友圈都快被小程序给刷爆了: 对于小程序这方面, 由于没有公测的资格.所以翻阅了许许多多的资料,来了解一下小程序: 微信小程序: 小程序是一种不需要下载安装即可使用的应用,它实现了应用&quo ...
- python字典排序取最值总结
dic = {"abc":18,"adc":19,"abe":20} # 默认对键排序,从小到大,返回排序后键组成的列表 zidian = ...