原题链接

算法不难,比赛的时候就和cyc大佬一起yy了正解,不过因为交的时候比较急(要回寝室惹),我有两数组开错大小直接爆到50,cyc大佬则只把文件输入关了一半,直接爆零(╯ ̄Д ̄)╯┻━┻

要尽量使\(k\)次删边都能有贡献,那么很容易就想到割边。

所以我们先用\(\mathtt{tarjan}\)跑出所有割边,而每一条割边都能产生\(1\)的贡献,因此有\(sum_{bridge}\)条割边,就能增加\(sum_{bridge}\)个连通块。

若\(k \leqslant sum_{bridge}\),那么最后的答案就是\(k + s\)(设原来的图有\(s\)个连通块)。

若\(k > sum_{bridge}\),那么先删去这些割边,则剩下\(k' = k - sum_{bridge}\)条删边的次数。

然后我们考虑剩下的图该怎么去删边使贡献最大。

很容易发现删去割边后的图就是单个简单环或多个以点连接的简单环的集合。

对于多个以点连接的简单环,实际上我们可以把这多个连接的环拆成多个单环,如下图所示:



因为虽然将多个环拆开来会多出几个点,但是与此同时也增加了相同数目的连通块,因此对拆开后的多个单环进行删边的最大贡献是与原图相同的。

然后我们考虑怎么在这单环集合中删边使得贡献最大化,显然对于一个环,总是要删去一条没有贡献的边使其变为一条链,之后的每一次删边都能产生\(1\)的贡献。

所以我们需要尽量减少第一次删去无贡献边的次数,那么贪心策略就显而易见了,即将所有环按环的大小(即边数)降序排序,然后逐个尝试删边,直到删边次数\(k'\)用光或是边被删光为止。

对于单个环删边产生的贡献,若\(k' > size_{circle}\)(设环的大小为\(size_{circle}\)),则贡献为\(size_{circle} - 1\);若\(k' <= size_{circle}\),则贡献为\(k' - 1\)。

至于如何找出多个环(已经找出割边并标记),我们可以用\(\mathtt{dfs}\),以时间戳标记的方式来找出,具体实现可以查看代码部分。

如果排序使用桶排,那么时间复杂度就是\(O(n + m)\),像我这种懒的人直接用\(\mathtt{sort}\)就会多一个\(\log\)。

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N = 1e6 + 10;
const int M = 2e6 + 10;
int fi[N], di[M << 1], ne[M << 1], bridge[M << 1], dfn[N], low[N], circle[N], cir_s, l = 1, ti, bridge_sum;
//fi,di,ne为邻接表,bridge储存某一边是否是割边,dfn为时间戳,low为tarjan中的追溯值,circle储存每个单环的大小。
bool fa_l[M << 1];//dfs中用来判断边是否已经走过
inline int re()//快读
{
int x = 0;
char c = getchar();
bool p = 0;
for (; c < '0' || c > '9'; c = getchar())
p |= c = '-';
for (; c >= '0' && c <= '9'; c = getchar())
x = x * 10 + c - '0';
return p ? -x : x;
}
inline void add(int x, int y) { di[++l] = y; ne[l] = fi[x]; fi[x] = l; }//加边
inline int minn(int x, int y) { return x < y ? x : y; }
bool comp(int x, int y) { return x > y; }
void tarjan(int x, int la)//tarjan找割边模板
{
int i, y;
dfn[x] = low[x] = ++ti;
for (i = fi[x]; i; i = ne[i])
if (!dfn[y = di[i]])
{
tarjan(y, i);
low[x] = minn(low[x], low[y]);
if (low[y] > dfn[x])
bridge[i] = bridge[i ^ 1] = 1, bridge_sum++;
}
else
if (i ^ la ^ 1)
low[x] = minn(low[x], dfn[y]);
}
void dfs(int x)//找出各个环
{
int i, y;
for (i = fi[x]; i; i = ne[i])
if (!bridge[i] && !fa_l[i])//如果这条边不是割边且没有走过
if (!dfn[y = di[i]])//如果边所连的点没有走过
dfn[y] = dfn[x] + 1, fa_l[i] = fa_l[i ^ 1] = 1, dfs(y);//标记时间戳;记录这条边已走过;继续搜索
else//如果走过了
fa_l[i] = fa_l[i ^ 1] = 1, circle[++cir_s] = dfn[x] + 1 - dfn[y];//同样要记录这条边已走过,否则在回溯的时候会出锅;将该环的大小计入数组
}
int main()
{
int i, n, m, k, x, y, ans = 0;
n = re(); m = re(); k = re();
for (i = 1; i <= m; i++)//输入图
{
x = re(); y = re();
add(x, y); add(y, x);
}
for (i = 1; i <= n; i++)//tarjan找割边,同时统计原图连通块的个数
if (!dfn[i])
tarjan(i, 0), ans++;
if (k <= bridge_sum)//若k不够删去所有割边,就直接输出答案
return printf("%d", k + ans), 0;
ans += bridge_sum; k -= bridge_sum;//统计答案并将k减去割边数
memset(dfn, 0, sizeof(dfn));
for (i = 1; i <= n; i++)//找环
if (!dfn[i])
dfn[i] = 1, dfs(i);
sort(circle + 1, circle + cir_s + 1, comp);
for (i = 1; i <= cir_s; i++)//贪心删边并统计贡献
if (k >= circle[i])
ans += circle[i] - 1, k -= circle[i];
else
{
ans += k - 1;
break;
}
return printf("%d", ans), 0;
}

牛客CSP-S提高组赛前集训营2 T2沙漠点列的更多相关文章

  1. 牛客网CSP-S提高组赛前集训营Round4

    牛客网CSP-S提高组赛前集训营 标签(空格分隔): 题解 算法 模拟赛 题目 描述 做法 \(BSOJ6377\) 求由\(n\)长度的数组复制\(k\)次的数组里每个连续子序列出现数字种类的和 对 ...

  2. 牛客CSP-S提高组赛前集训营1

    牛客CSP-S提高组赛前集训营1 比赛链接 官方题解 before:T1观察+结论题,T2树形Dp,可以换根或up&down,T3正解妙,转化为图上问题.题目质量不错,但数据太水了~. A-仓 ...

  3. 牛客CSP-S提高组赛前集训营3

    A 货物收集 显然是一个二分答案的题. #include<iostream> #include<cstdio> #include<cstring> #include ...

  4. 牛客CSP-S提高组赛前集训营3 赛后总结

    货物收集 二分答案.复杂度\(O(n\log n)\). 货物分组 用费用提前计算的思想,考虑用一个新的箱子来装货物会发生什么. 显然费用会加上后面的所有货物的总重. \(60\)分的\(O(n^2) ...

  5. 牛客CSP-S提高组赛前集训营2 ———— 2019.10.31

    比赛链接 期望得分:100+20+20 实际得分:40+20+30 awa  cccc T1 :基于贪心的思路,然后开始爆搜(雾 那必然是会死的,好吧他就是死了 #include<iostrea ...

  6. 牛客CSP-S提高组赛前集训营1———2019.10.29 18:30 至 22:00

    期望得分:100+0+10 实际得分:40+0+0 考炸了... T1:题目链接 究竟为什么会这样,,, 仔细研读我的丑代码 发现... 枯辽.... #include<cstdio> # ...

  7. 20191029 牛客CSP-S提高组赛前集训营1

    前一个小时看这几道题感觉要爆零 A. 仓鼠的石子游戏 分析一下发现a[i]>1a[i]>1a[i]>1时后先手必输,a[i]=1a[i]=1a[i]=1时先手必赢 然后直接看1的个数 ...

  8. 牛客CSP-S提高组赛前集训营4 赛后总结

    复读数组 分成 3 种区间算答案: 一个块内的区间 两个块交界处,长度小于块长的区间 长度不小于块长的区间 对于第三种区间,容易发现每个区间的权值一样,只需要算出个数即可. 对于前两种空间,我的思路是 ...

  9. 牛客CSP-S提高组赛前集训营5 赛后总结

    A.无形的博弈 心理题. 答案为\(2^n\),可感性理解结论的正确性. #include<bits/stdc++.h> #define LL long long const LL Mod ...

随机推荐

  1. VS2017 远程调试linux(centos).net core程序(通过附加程序的方式)

    参考两位大神的帖子: https://blog.csdn.net/soband_xiang/article/details/82914195 https://blog.csdn.net/weixin_ ...

  2. cesium地下模式(地表透明)2

    接上一篇博客,这篇直接分析火星的源码,看它到底改了些什么. 注意:在cesium1.63.1版本改变了模块化方式,由AMD改为ES6模块化.注意引入文件加载模块时做出对应修改. 1.火星代码里修改了4 ...

  3. 织梦一二级导航菜单被点击顶级栏目高亮(加class)解决方法

    织梦一二级导航菜单被点击的栏目高亮显示方法详解,废话不多说直接举例说明: 织梦一级菜单被点击栏目高亮调用方法: {dede:channel typeid ='1'  type ='son' curre ...

  4. 剑指offer:把数组排成最小的数

    题目描述: 输入一个正整数数组,把数组里所有数字拼接起来排成一个数,打印能拼接出的所有数字中最小的一个.例如输入数组{3,32,321},则打印出这三个数字能排成的最小数字为321323. 思路分析: ...

  5. java.lang.UnsupportedOperationException 异常分析

    今天将一个数组转换成 List 然后进行 remove 操作时却抛出 java.lang.UnsupportedOperationException 异常. String pattern = &quo ...

  6. 【转载】 tf.Print() (------------ tensorflow中的print函数)

    原文地址: https://blog.csdn.net/weixin_36670529/article/details/100191674 ------------------------------ ...

  7. docker 安装jenkins 发布 asp.net core 2.0

    安装Docker 其实安装Docker的过程,大家可以到Docker官网找到自己相对应的安装文档进行安装,Docker区分CE和EE的两个版本,具体这两个版本有什么区别,大家自行查阅相关资料,这里不再 ...

  8. word中的总页数不包括封面、目录

    删除分隔符:选项-显示-显示所有格式标记

  9. MyCat不支持毫秒 bug fix

    问题描述:mysql jdbc的驱动(mysql-connector-java-5.1.34.jar)设置的服务器的版本号最低是5.6.4才不会截取时间毫秒,但是现在取的是mycat 的版本号 5.5 ...

  10. JAVA中List对象去除重复值的方法

    JAVA中List对象去除重复值,大致分为两种情况,一种是List<String>.List<Integer>这类,直接根据List中的值进行去重,另一种是List<Us ...