bzoj 1977
题意:求严格的次小生成树。点n<=100000,m<=300000
思路:很容易想到先做一边最小生成树,然后枚举每条非树边(u, v, w),然后其实就是把u,v路径上小于w的最大边替换成w,对于所有的这种新树取一个权值最小的即可。。
然后就变成求u,v的最大值及次大值。。树链剖分和lct显然是可以做的。。
不过很早就知道倍增却一直没写过,今天就正好写一发。。
code:
/**************************************************************
Problem: 1977
User: yzcstca
Language: C++
Result: Accepted
Time:2344 ms
Memory:35048 kb
****************************************************************/ #include<cstdio>
#include<iostream>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<algorithm>
#include<string>
#include<map>
#include<set>
#include<vector>
#include<queue>
#include<stack>
#include<ctime>
#define repf(i, a, b) for (int i = (a); i <= (b); ++i)
#define M0(x) memset(x, 0, sizeof(x))
#define vii vector< pair<int, int> >::iterator
#define x first
#define y second
#define two(i) (1 << i)
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
const int maxn = ;
const int maxm = ;
struct oo{
int u, v, w;
bool operator<(const oo& p) const{
return w < p.w;
}
} E[maxm];
vector<pii> e[maxn];
int fa[maxn], intree[maxm];
int n, m, ans;
int dep[maxn], f[maxn][], vv[maxn][][]; void init(){
for (int i = ; i < m; ++i)
scanf("%d%d%d", &E[i].u, &E[i].v, &E[i].w);
repf(i, , n) e[i].clear();
} inline int find(const int& k){
return fa[k] == k ? k : fa[k] = find(fa[k]);
} int vis[maxn], t[maxn];
void bfs(){
M0(vis);
queue<int> q;
q.push(), dep[] = , vis[] = ;
int u, v, cnt;
t[cnt = ] = ;
while (!q.empty()){
u = q.front();
q.pop();
for (vii it = e[u].begin(); it != e[u].end(); ++it){
if (vis[it->x]) continue;
dep[it->x] = dep[u] + ;
f[it->x][] = u;
vv[it->x][][] = it->y, vv[it->x][][] = -;
q.push(it->x), vis[it->x] = , t[++cnt] = it->x;
}
}
} void update(int v[],const int& val){
if (val > v[])
swap(v[], v[]), v[] = val;
else if (val < v[] && val > v[])
v[] = val;
} void rmq(){
int u, v;
repf(i, , n){
u = t[i];
for (int i = ; i <= ; ++i){
if (two(i) > dep[u]) break;
v = f[u][i-];
f[u][i] = f[v][i-];
vv[u][i][] = vv[u][i][] = -;
update(vv[u][i], vv[u][i-][]);
update(vv[u][i], vv[u][i-][]);
update(vv[u][i], vv[v][i-][]);
update(vv[u][i], vv[v][i-][]);
}
}
} int res[];
void query(int u, int s, int res[]){
for (int i = ; i <= ; ++i) if (s & two(i))
update(res, vv[u][i][]), update(res, vv[u][i][]), u = f[u][i], s ^= two(i);
} void work(int u, int v, const int& w){
if (dep[u] > dep[v]) swap(u, v);
int fu = u, fv = v, h;
if (dep[fu] != dep[fv]){
h = dep[fv] - dep[fu];
for (int i = ; i <= ; ++i) if (h & two(i))
h ^= two(i), fv = f[fv][i];
}
if (fu == fv){
res[] = res[] = -;
query(v, dep[v] - dep[u], res);
h = (res[] != w) ? res[] : res[];
if (h != -) ans = min(ans, w - h);
return;
}
for (int i = ; i >= ; --i){
if (two(i) > dep[fu]) continue;
if (f[fu][i] != f[fv][i])
fu = f[fu][i], fv = f[fv][i];
}
fu = f[fu][];
res[] = res[] = -;
query(u, dep[u] - dep[fu], res);
query(v, dep[v] - dep[fu], res);
h = (res[] != w) ? res[] : res[];
if (h != -) ans = min(ans, w - h);
} void solve(){
repf(i, , n) fa[i] = i;
sort(E, E + m);
memset(intree, , sizeof(int) * (m + ));
int u, v, fu, fv, w;
ll mst = ;
repf(i, , m-){
u = E[i].u, v = E[i].v, w = E[i].w;
fu = find(u), fv = find(v);
if (fu != fv){
fa[fu] = fv, intree[i] = ;
mst += E[i].w;
e[u].push_back(make_pair(v, w));
e[v].push_back(make_pair(u, w) );
}
}
bfs();
rmq();
ans = 0x3fffffff;
for (int i = ; i < m; ++i) if (!intree[i])
work(E[i].u, E[i].v, E[i].w);
cout << mst + ans << endl; } int main(){
// freopen("a.in", "r", stdin);
// freopen("a.out", "w", stdout);
while (scanf("%d%d", &n, &m) != EOF){
init();
solve();
}
return ;
}
bzoj 1977的更多相关文章
- BZOJ 1977: [BeiJing2010组队]次小生成树 Tree( MST + 树链剖分 + RMQ )
做一次MST, 枚举不在最小生成树上的每一条边(u,v), 然后加上这条边, 删掉(u,v)上的最大边(或严格次大边), 更新答案. 树链剖分然后ST维护最大值和严格次大值..倍增也是可以的... - ...
- bzoj 1977 [BeiJing2010组队]次小生成树 Tree
题目:https://www.lydsy.com/JudgeOnline/problem.php?id=1977 kruscal别忘了先按边权sort.自己觉得那部分处理得还挺好的.(联想到之前某题的 ...
- [BeiJing2010组队][BZOJ 1977]次小生成树 Tree
话说这个[BeiJing2010组队]是个什喵玩意? 这是一道严格次小生成树,而次小生成树的做法是层出不穷的 MATO IS NO.1 的博客里对两种算法都有很好的解释,值得拥有: (果然除我以外, ...
- BZOJ 1977 次小生成树
TM终于过了.... #include<iostream> #include<cstdio> #include<cstring> #include<algor ...
- BZOJ 1977 次小生成树(最近公共祖先)
题意:求一棵树的严格次小生成树,即权值严格大于最小生成树且权值最小的生成树. 先求最小生成树,对于每个不在树中的边,取两点间路径的信息,如果这条边的权值等于路径中的权值最大值,那就删掉路径中的次大值, ...
- BZOJ 1977 严格次小生成树(算竞进阶习题)
树上倍增+kruskal 要找严格次小生成树,肯定先要找到最小生成树. 我们先把最小生成树的边找出来建树,然后依次枚举非树边,容易想到一种方式: 对于每条非树边(u,v),他会与树上的两个点构成环,我 ...
- BZOJ 1977[BeiJing2010组队]次小生成树 Tree - 生成树
描述: 就是求一个次小生成树的边权和 传送门 题解 我们先构造一个最小生成树, 把树上的边记录下来. 然后再枚举每条非树边(u, v, val),在树上找出u 到v 路径上的最小边$g_0$ 和 严格 ...
- 【刷题】BZOJ 1977 [BeiJing2010组队]次小生成树 Tree
Description 小 C 最近学了很多最小生成树的算法,Prim 算法.Kurskal 算法.消圈算法等等. 正当小 C 洋洋得意之时,小 P 又来泼小 C 冷水了.小 P 说,让小 C 求出一 ...
- BZOJ 1977 严格次小生成树
小C最近学了很多最小生成树的算法,Prim算法.Kurskal算法.消圈算法等等.正当小C洋洋得意之时,小P又来泼小C冷水了.小P说,让小C求出一个无向图的次小生成树,而且这个次小生成树还得是严格次小 ...
随机推荐
- operator 的两种用法
C++,有时它的确是个耐玩的东东,就比如operator,它有两种用法,一种是operator overloading(操作符重载),一种是operator casting(操作隐式转换).1.操作符 ...
- Ms sql pivot unpivot
--建表 create table dbo.orders ( orderid int not null primary key nonclustered, orderdate datetime not ...
- js编码
var url = encodeURI(encodeURI("search-keyword-"+keyword+".html")); 后台uri = Strin ...
- TIJ读书笔记08-数组的初始化和可变长参数形参
TIJ读书笔记08-数组的初始化和可变参数形参 数组 数组的声明 数组的初始化和赋值 可变参数列表 数组 相同类型的,用一个标识符名称封装到一起的一个对象序列或者基本数据类型序列叫数组.(多么严谨的概 ...
- jsoncpp操作 json
jsoncpp操作 json 博客分类: c/c++ object-c 之 iphone #include <iostream> //#include "json/json. ...
- PHP基于SOAP实现webservice
简单对象访问协议(SOAP)是一种轻量的.简单的.基于 XML 的协议,它被设计成在 WEB 上交换结构化的和固化的信息. SOAP 可以和现存的许多因特网协议和格式结合使用,包括超文本传输协议( H ...
- left join ,right join ,inner join ,cross join 区别
left join ,right join ,inner join ,cross join 区别(参考:http://zhidao.baidu.com/link?url=gpOl9HXZR0OrQuy ...
- 实践一:Linux基础实践
一.Linux基础实践 1.1 1. 掌握软件源的维护方法,配置系统使用软件源镜像.掌握通过软件源来查找,安装,卸载,更新软件的方法. 这部分内容在许多学长学姐的报告里都有很详细的讲解,我在此就不赘述 ...
- java异常和spring事务注解
http://www.techferry.com/articles/spring-annotations.html http://www.oschina.net/question/2367675_23 ...
- 用window.showModelDialog() 打开的页面的返回值
有两个页面也个 Default1.aspx 另外一个是 Default2.aspx Default1.aspx 有个按钮是用来打开Default2.aspx页面的 按钮的js代码是 var win ...