HDU 6041 - I Curse Myself | 2017 Multi-University Training Contest 1
和题解大致相同的思路
/*
HDU 6041 - I Curse Myself [ 图论,找环,最大k和 ] | 2017 Multi-University Training Contest 1
题意:
给出一个仙人掌图,求最小的k棵生成树
N <= 1000, M <= 2000, K <= 1e5
分析:
将问题转化为从每个环中选出一条边,求最大的k个和
首先找环,随便怎么找,比如 在保证每条边只走一次的情况下遍历到祖先节点就说明有环,记录一下前驱就能找出来
然后是每个环两两合并,此时相当于两个vector,res.size() = k,cir[i].size() = Mi, ∑mi = M
由于 M<=2000,k <= 1e5,故选择在堆中的元素是cir[i]中的元素
这样就能保证复杂度为 O( ∑K*log(mi) ) = O( K*log(∏mi) ) <= O(K*M) 编码时长: INF(-8)
*/
#include <bits/stdc++.h>
using namespace std;
#define LL long long
const LL MOD = 1LL<<32;
const int N = 1005;
struct Edge {
int to, next, w;
bool vis;
}edge[N<<2];
int head[N], tot;
void init() {
memset(head, -1, sizeof(head));
tot = 0;
}
void addedge(int u, int v, int w) {
edge[tot].to = v; edge[tot].next = head[u];
edge[tot].vis = 0; edge[tot].w = w;
head[u] = tot++;
}
int n, m, block;
vector<int> cir[N];
bool vis[N];
int f[N], g[N];//父节点和连着的边
void dfs(int u)
{
for (int i = head[u]; i != -1; i = edge[i].next)
{
int v = edge[i].to;
if (edge[i].vis) continue;
edge[i].vis = 1;
edge[i^1].vis = 1;
if (vis[v])
{
++block;
int t = u;
do{
cir[block].push_back(edge[g[t]].w);
t = f[t];
}while (t != v);
cir[block].push_back(edge[i].w);
}
else
{
vis[v] = 1, f[v] = u, g[v] = i;
dfs(v);
}
}
}
void find_cir()
{
for (int i = 0; i < N; i++) cir[i].clear();
memset(vis, 0, sizeof(vis));
block = 0;
vis[1] = 1;
dfs(1);
}
struct Node {
int x, id;
bool operator < (const Node& b) const {//大的先出去
return x < b.x;
}
};
priority_queue<Node> Q;
vector<int> res, tmp;
void solve(int k)
{
res.clear();
res.push_back(0);
for (int i = 1; i <= block; ++i)
{
while (!Q.empty()) Q.pop();
tmp.clear();
for (auto& x : cir[i])
Q.push(Node{x+res[0], 0});
while (tmp.size() < k && !Q.empty())
{
auto p = Q.top(); Q.pop();
tmp.push_back(p.x);
if (p.id+1 < res.size())
{
++p.id;
p.x += res[p.id] - res[p.id-1];
Q.push(p);
}
}
res.clear();
for (auto& x : tmp) res.push_back(x);
}
}
int main()
{
int tt = 0, u, v, w, k;
while (~scanf("%d%d", &n, &m))
{
init();
LL sum = 0, ans = 0;
for (int i = 1; i <= m; i++)
{
scanf("%d%d%d", &u, &v, &w);
sum += w;
addedge(u, v, w); addedge(v, u, w);
}
find_cir();
scanf("%d", &k);
solve(k);
for (int i = 0; i < res.size(); i++)
ans += (i+1) * ( (sum-res[i])) % MOD;
printf("Case #%d: %lld\n", ++tt, ans%MOD);
}
}
限制第k小的大小后,满足这个限制的答案的数量具有单调性,故还可以二分第k小 暴力DFS+剪枝 验证,就是代码不算好写
#include <bits/stdc++.h>
using namespace std;
#define LL long long
const int N = 1005;
const LL MOD = 1LL<<32;
struct Edge {
int to, next, w;
bool vis;
}edge[N<<2];
int head[N], tot;
void init() {
memset(head, -1, sizeof(head));
tot = 0;
}
void addedge(int u, int v, int w) {
edge[tot].w = w; edge[tot].to = v; edge[tot].next = head[u];
edge[tot].vis = 0;
head[u] = tot++;
}
int block;
vector<int> cir[N];
int f[N], g[N], vis[N];
void dfs(int u)
{
for (int i = head[u]; ~i; i = edge[i].next)
{
if (edge[i].vis) continue;
edge[i].vis = 1;
edge[i^1].vis = 1;
int v = edge[i].to;
if (vis[v])
{
++block;
int t = u;
do {
cir[block].push_back(edge[g[t]].w);
t = f[t];
}while (t != v);
cir[block].push_back(edge[i].w);
}
else
{
vis[v] = 1;
f[v] = u, g[v] = i;
dfs(v);
}
}
}
void find_cir()
{
for (int i = 0; i < N; i++) cir[i].clear();
memset(vis, 0, sizeof(vis));
block = 0;
vis[1] = 1;
dfs(1);
}
bool cmp (const int &x, const int &y) {
return x > y;
}
int n, m, k, num;
LL mid, res[100005];
void dfs(int i, LL sum)
{
if (num > k) return;
if (sum > mid) return;
if (i == block+1)
{
res[++num] = sum; return;
}
for (auto& x : cir[i])
{
if (sum + x > mid) break;
dfs(i+1, sum+x);
}
}
LL BinaryFind(LL l, LL r)
{
while (l <= r)
{
mid = (l+r)>>1;
num = 0;
dfs(1, 0);
if (num >= k) r = mid-1;
else l = mid+1;
}
return l;
}
int main()
{
int t = 0, x, y, z;
while (~scanf("%d%d", &n, &m))
{
init();
LL base = 0, ans = 0;
for (int i = 1; i <= m; i++)
{
scanf("%d%d%d", &x, &y, &z);
addedge(x, y, z), addedge(y, x, z);
base += z;
}
find_cir();
scanf("%d", &k);
printf("Case #%d: ", ++t);
if (block == 0)
{
printf("%lld\n", base%MOD); continue;
}
for (int i = 1; i <= block; i++)
{
sort(cir[i].begin(), cir[i].end(), cmp);
base -= cir[i][0];
for (int j = 1; j < cir[i].size(); j++)
cir[i][j] = cir[i][0] - cir[i][j];
cir[i][0] = 0;
}
int Max = 1;
for (int i = 1; i <= block && Max < k; i++)
Max *= cir[i].size();
if (k > Max)
{
k = Max;
mid = 2e9;
num = 0;
dfs(1, 0);
}
else
{
mid = BinaryFind(1, 2e9);
mid--;
num = 0;
dfs(1, 0);
for (int i = num+1; i <= k; i++) res[i] = mid+1;
}
sort(res+1, res+k+1);
for (int i = 1; i <= k; i++)
ans += i * (base+res[i]) % MOD;
printf("%lld\n", ans% MOD);
}
}
HDU 6041 - I Curse Myself | 2017 Multi-University Training Contest 1的更多相关文章
- HDU 6041.I Curse Myself 无向仙人掌图
I Curse Myself Time Limit: 8000/4000 MS (Java/Others) Memory Limit: 131072/131072 K (Java/Others) ...
- hdu 6041 I Curse Myself
题目: 点这里OvO http://acm.hdu.edu.cn/showproblem.php?pid=6041 2017 Multi-University Training Contest - T ...
- hdu 6041 I Curse Myself 无向图找环+优先队列
I Curse Myself Time Limit: 8000/4000 MS (Java/Others) Memory Limit: 131072/131072 K (Java/Others) ...
- HDU 6162 - Ch’s gift | 2017 ZJUT Multi-University Training 9
/* HDU 6162 - Ch’s gift [ LCA,线段树 ] | 2017 ZJUT Multi-University Training 9 题意: N节点的树,Q组询问 每次询问s,t两节 ...
- HDU 6041 I Curse Myself(点双联通加集合合并求前K大) 2017多校第一场
题意: 给出一个仙人掌图,然后求他的前K小生成树. 思路: 先给出官方题解 由于图是一个仙人掌,所以显然对于图上的每一个环都需要从环上取出一条边删掉.所以问题就变为有 M 个集合,每个集合里面都有一堆 ...
- HDU 6041 I Curse Myself(二分+搜索)
[题目链接] http://acm.hdu.edu.cn/showproblem.php?pid=6041 [题目大意] 给出一个仙人掌图,求第k小生成树 [题解] 首先找到仙人掌图上的环,现在的问题 ...
- HDU 6041 I Curse Myself ——(仙人掌图,tarjan,转化)
题解见这个博客:http://blog.csdn.net/ME495/article/details/76165039. 复杂度不太会算..这个经典问题的解法需要注意,维护队列里面只有k个元素即可.另 ...
- 2017 Wuhan University Programming Contest (Online Round) Lost in WHU 矩阵快速幂 一个无向图,求从1出发到达n最多经过T条边的方法数,边可以重复经过,到达n之后不可以再离开。
/** 题目:Lost in WHU 链接:https://oj.ejq.me/problem/26 题意:一个无向图,求从1出发到达n最多经过T条边的方法数,边可以重复经过,到达n之后不可以再离开. ...
- 2017 Wuhan University Programming Contest (Online Round) C. Divide by Six 分析+模拟
/** 题目:C. Divide by Six 链接:https://oj.ejq.me/problem/24 题意:给定一个数,这个数位数达到1e5,可能存在前导0.问为了使这个数是6的倍数,且没有 ...
随机推荐
- 19牛客暑期多校 round1 A 有关笛卡尔树的结论
题目传送门//res tp nowcoder 分析 定理:B1~B2当且仅当B1与B2有同构的笛卡尔树. (B₁~B₂ iff B₁ and B₂ have isomorphic Cartesian ...
- java进程CPU高分析
JVM导致系统CPU高的常见场景: 内存不足,JVM gc频繁,一般会伴随OOMJVM某个线程死循环或者递归调用 定位和解决1.内存不足,gc频繁可参考我的这遍文章解决.https://blog.cs ...
- pb datawindow 类型
DataWindow.Processing 判断 DataWindow 对象的类型 可用 DataWindow.Processing 判断 DataWindow 对象的类型,dw的类型如下: ...
- 算法:二叉树的层次遍历(递归实现+非递归实现,lua)
二叉树知识参考:深入学习二叉树(一) 二叉树基础 递归实现层次遍历算法参考:[面经]用递归方法对二叉树进行层次遍历 && 二叉树深度 上面第一篇基础写得不错,不了解二叉树的值得一看. ...
- 怎样终止(杀掉) Linux 中的进程?
使用 kill -9 进程号 命令, 可是强行终止该进程. 如果使用直接使用 kill 进程号 命令, 则会让进程 "自行了断" . 因此, 一般是 kill -9 进程号 用得较 ...
- 【已解决】Field injection is not recommended和Could not autowired. No beans of 'xxx' type found.
目录 问题 解决办法 备注 问题 在项目中,我们使用Spring的@Autowired注解去引入其他类时有时候阿里的编码规约插件就会提示:"Field injection is not re ...
- 手把手教你搭建FastDFS集群(中)
手把手教你搭建FastDFS集群(中) 版权声明:本文为博主原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明. 本文链接:https://blog.csdn.net/u0 ...
- css高度居中
1.已知元素高度 // 子盒子 #div1{ width:200px; height:200px; position: absolute; //父元素需要相对定位 top: 50%; left: 50 ...
- app欢迎页问题
今天替换app中的图片,打包成apk后,欢迎页的图片怎么替换都还是旧的,尝试多次以后,确定以及肯定是替换成功了的,而且替换的也都对,只好清理了一下项目,重新build,最后再打包,结果成功了!真是坑! ...
- css and canvas实现圆形进度条
进度条效果: 话不多说,上代码 使用css动画实现,看到一篇博客的启发,稍微修改了下, css实现的原理是用两个半圆一开始隐藏,再分别旋转180度,最后成为一个整圆 半圆效果,一开始右边的半圆在盒 ...