和题解大致相同的思路

/*
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的更多相关文章

  1. HDU 6041.I Curse Myself 无向仙人掌图

    I Curse Myself Time Limit: 8000/4000 MS (Java/Others)    Memory Limit: 131072/131072 K (Java/Others) ...

  2. hdu 6041 I Curse Myself

    题目: 点这里OvO http://acm.hdu.edu.cn/showproblem.php?pid=6041 2017 Multi-University Training Contest - T ...

  3. hdu 6041 I Curse Myself 无向图找环+优先队列

    I Curse Myself Time Limit: 8000/4000 MS (Java/Others)    Memory Limit: 131072/131072 K (Java/Others) ...

  4. 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两节 ...

  5. HDU 6041 I Curse Myself(点双联通加集合合并求前K大) 2017多校第一场

    题意: 给出一个仙人掌图,然后求他的前K小生成树. 思路: 先给出官方题解 由于图是一个仙人掌,所以显然对于图上的每一个环都需要从环上取出一条边删掉.所以问题就变为有 M 个集合,每个集合里面都有一堆 ...

  6. HDU 6041 I Curse Myself(二分+搜索)

    [题目链接] http://acm.hdu.edu.cn/showproblem.php?pid=6041 [题目大意] 给出一个仙人掌图,求第k小生成树 [题解] 首先找到仙人掌图上的环,现在的问题 ...

  7. HDU 6041 I Curse Myself ——(仙人掌图,tarjan,转化)

    题解见这个博客:http://blog.csdn.net/ME495/article/details/76165039. 复杂度不太会算..这个经典问题的解法需要注意,维护队列里面只有k个元素即可.另 ...

  8. 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之后不可以再离开. ...

  9. 2017 Wuhan University Programming Contest (Online Round) C. Divide by Six 分析+模拟

    /** 题目:C. Divide by Six 链接:https://oj.ejq.me/problem/24 题意:给定一个数,这个数位数达到1e5,可能存在前导0.问为了使这个数是6的倍数,且没有 ...

随机推荐

  1. sqlserver中分页的方式

    1.使用top进行: 1.select top 页大小 * from 表名where id not in(select top 页大小*(查询第几页-1) id from 表名 order by id ...

  2. VC/MFC 进程间通信方法总结

    摘   要   随着人们对应用程序的要求越来越高,单进程应用在许多场合已不能满足人们的要求.编写多进程 / 多线程程序成为现代程序设计的一个重要特点,在多进程程序设计中,进程间的通信是不可避免的. M ...

  3. echart4数据管理组件dataset学习

    背景 如果后台数据固定,如何动态定制其前端数据展示方式呢?也就是说同一种数据,如何被多个前端Echarts图表复用呢?最近在研究一种数据展示可配置化的功能,然后发现了echart4.0的dataset ...

  4. iView组件Tabs嵌套使用

    <Tabs name="tab" value="a"> <TabPane label="标签a" name="a ...

  5. js修改当前页面地址栏参数

    利用HTML5 history新特性replaceState方法可以修改当前页面地址栏参数,示例代码: //选择日期后改变地址栏 var urlSearch = location.href; var ...

  6. JS基础_基本数据类型和引用数据类型

    <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title> ...

  7. java中成员变量和局部变量在内存中的分配

    对于成员变量和局部变量:成员变量就是方法外部,类的内部定义的变量:局部变量就是方法或语句块内部定义的变量.局部变量必须初始化. 形式参数是局部变量,局部变量中基础数据类型的引用和值都存储在栈中,对象引 ...

  8. O054、Attach Volume 操作(Part II)

    参考https://www.cnblogs.com/CloudMan6/p/5631328.html     计算节点作为iSCSI initiator 访问存储节点 iSCSI Target 上的v ...

  9. PS 中混合模式

    1.正常模式 2. 溶解 3. 变暗    :  把两幅图中较暗的区域显示出来 4.正片叠底   总体变暗,把图层中较浅的颜色由下一图层较深的颜色显现(和滤色相反) 7. 深色  取较小的颜色 8. ...

  10. centos 配置rsync+inotify数据实时同步

    何为rsync? 定义: rsync是一个开源的快速备份工具,可以在不同主机之间镜像同步整个目录树,支持增量备份,保持链接和权限,非常适用于异地备份 何为源端和发起端? 在远程同步过程中,负责发起rs ...