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的倍数,且没有 ...
随机推荐
- MongoDB对数组元素及内嵌文档进行增删改查操作
比如我有一个user类,他包含一个标签属性,这个标签是一个数组,数组里面的元素是内嵌文档,格式如下: { "_id" : "195861", &qu ...
- SQLite进阶-12.Distinct关键字
目录 DISTINCT关键字 DISTINCT关键字 DISTINCT关键字与SELECT语句一起使用,用来消除重复数据,获得唯一数据. -- 语句 SELECT DISTINCT column1, ...
- LC 752 Open the Lock
由于这个问题,涉及了很多知识,例如数据结构里面的哈希表,c++中的迭代器,因此,需要对于每一个疑惑逐一击破. 问题描述 You have a lock in front of you with 4 c ...
- # [Poj 3107] Godfather 链式前向星+树的重心
[Poj 3107] Godfather 链式前向星+树的重心 题意 http://poj.org/problem?id=3107 给定一棵树,找到所有重心,升序输出,n<=50000. 链式前 ...
- Elastic Search中Document的CRUD操作
一. 新增Document在索引中增加文档.在index中增加document.ES有自动识别机制.如果增加的document对应的index不存在.自动创建,如果index存在,type不存在自动创 ...
- 并不对劲的CF1237D&E:Balanced Playlist and Binary Search Trees
CF1237D Balanced Playlist 题意 有一个长度为\(n\)(\(n\leq 10^5\))的循环播放歌单,每首歌有一个优秀值\(a_i\)(\(a_i\leq 10^9\)). ...
- [POI2012]ROZ-Fibonacci Representation (贪心)
大意: 给定数$n$, 求将$n$划分为最少的斐波那契数的和或差. 每次取相邻$n$的斐波那契数一定最优, 考虑证明. 结论1:存在一个最优解,使得每个斐波那契数使用不超过1次.(考虑$2F_n=F_ ...
- Java EE javax.servlet.http中的HttpRequest抽象类
HttpRequest抽象类 public abstract class HttpServlet extends GenericServlet 实现的接口有:Serializable, Servlet ...
- shiro学习(一)
基础依赖: shiro-core,junit(因为在单元测试中) test.class public class AuthenticationTest { SimpleAccountRealm rea ...
- O062、NFS Volume Provider(Part I)
参考https://www.cnblogs.com/CloudMan6/p/5683352.html cinder-volume 支持多种 volume provider ,前面我们一直使用的是默 ...