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的倍数,且没有 ...
随机推荐
- SQLYOG如何将数据导出excel格式
方法/步骤 如图,笔者的数据库中有一张student表,想把这张表中的数据导出成excel 在这张表上右击,选择“Export”,再选择“Export Table Data as CSV, ...
- 03 Python基础
1.输出和输入 (1)print打印 Python 提供print方法来打印信息 输入: print ("hello python") 调用print方法,用户双引号(" ...
- Zookeeper 配置和原理探究
一 Zookeeper是什么? 服务集群对外提供服务的过程中,有很多的配置需要随时更新,服务间需要协调工作,那么这些信息如何推送到各个节点?并且保证信息的一致性和可靠性?我们知道分布式协调服务很难正确 ...
- pt工具
percona-toolkit简介percona-toolkit是一组高级命令行工具的集合,用来执行各种通过手工执行非常复杂和麻烦的mysql任务和系统任务,这些任务包括: 检查master和slav ...
- 21-Perl 发送邮件
1.Perl 发送邮件如果你的程序在 Linux/Unix 系统上运行,你就可以在 Perl 中使用 sendmail 工具来发送邮件.以下是一个简单的脚本实例用于发送邮件:#!/usr/bin/pe ...
- aspose导出数据
注意 aspose合并单元格后设置单元格样式要一格一格的设置 public class InvoiceAsposeExcel { /// <summary> /// 导出数据 /// &l ...
- BZOJ2456-mode题解--一道有趣题
题目链接: https://www.lydsy.com/JudgeOnline/problem.php?id=2456 瞎扯 这是今天考的模拟赛T2交互题的一个30分部分分,老师在讲题时提到了这题.考 ...
- 第一章 PHP mySQL
一,服务器环境搭建 1-1.Apache服务器.(端口号定义,http协议,开启和关闭) 服务器: 我们在浏览器浏览网页的时候,在地址栏中都会产生一个url.通过这个url,浏览器从互联网中找到一个网 ...
- 对MySQL索引、锁及事务的简单分析
一.索引的数据结构 1.二叉搜索树实现的索引 二叉搜索树如下图,它查找元素的时间复杂度为O(logn) 但如果经常出现增删操作,最后导致二叉搜索树变成线性的二叉树,这样它查找元素的时间复杂度就会变成O ...
- Maxwell平滑升级流程
1. 同步配置文件 copy之前的配置文件 2 关掉监控程序 如果有的话 3 kill掉之前的maxwell程序 4 查询已经读取到的position位置,然后重启服务 ...