这道题以前zbtrs大佬给我讲过。但是我只知道思想,不知道要lct维护...

这个套路很常见。

题意:给你一个无向图,每条边有a,b两个权值。求1到n号点的一条路径,路径的权值是每条边的最大a与最大b之和。求可能的最小权值。无解输出-1。

解:有个很朴素的想法是爆搜......

有个很朴素(??)的想法,最后路径的a最大值一定是某条边的a,于是我们枚举这个a,每次把小于a的边都加进去,然后若1,n连通就跑b的最短路。这样就有50分了。

然后我们发现每次跑最短路的时间不可承受,如何优化呢?

我们肯定会在某一时刻加出环,然后我们发现这个环上的a的大小是无关紧要的,我们把环上b最大的边去掉,这样就是一棵树了。

然后用lct实行这个操作,时间复杂度mlog(n + m)。

注意这里没有点权,只有边权,而且边全都给出来了,我们就对于每个边新建一个点代表它就行了...具体看代码。

 // NOI 2014 mofa forest
#include <cstdio>
#include <algorithm> const int N = , M = , INF = 0x7f7f7f7f; inline void read(int &x) {
char c = getchar();
bool f = ;
while(c < '' || c > '') {
if(c == '-') {
f = ;
}
c = getchar();
}
while(c >= '' && c <= '') {
x = (x << ) + (x << ) + c - ;
c = getchar();
}
if(f) {
x = -x;
}
return;
} struct Edge {
int u, v;
int a, b;
inline bool operator <(const Edge &d) const {
if(a != d.a) {
return a < d.a;
}
return b < d.b;
}
}edge[M]; struct UFS {
int fa[N + M]; inline void clear() {
for(int i = ; i < N + M; i++) {
fa[i] = i;
}
return;
} UFS() {
clear();
} inline int find(int x) {
if(fa[x] == x) {
return x;
}
return fa[x] = find(fa[x]);
} inline void merge(int x, int y) {
fa[find(x)] = find(y);
return;
} inline bool check(int x, int y) {
return find(x) == find(y);
}
}ufs; struct LCT {
int val[N + M], fa[N + M], s[N + M][], large[N + M], stk[N + M], t;
bool rev[N + M]; inline int no_root(int x) {
return s[fa[x]][] == x || s[fa[x]][] == x;
} inline void pushup(int x) {
large[x] = x;
if(val[large[x]] < val[large[s[x][]]]) {
large[x] = large[s[x][]];
}
if(val[large[x]] < val[large[s[x][]]]) {
large[x] = large[s[x][]];
}
return;
} inline void pushdown(int x) {
if(!rev[x]) {
return;
}
if(s[x][]) {
rev[s[x][]] ^= ;
}
if(s[x][]) {
rev[s[x][]] ^= ;
}
std::swap(s[x][], s[x][]);
rev[x] = ;
return;
} inline void rotate(int x) {
int y = fa[x];
int z = fa[y];
bool f = (s[y][] == x); fa[x] = z;
if(no_root(y)) {
s[z][s[z][] == y] = x;
}
s[y][f] = s[x][!f];
fa[s[x][!f]] = y;
s[x][!f] = y;
fa[y] = x; pushup(y);
pushup(x);
return;
} inline void splay(int x) {
int y = x;
stk[++t] = x;
while(no_root(y)) {
y = fa[y];
stk[++t] = y;
}
while(t) {
pushdown(stk[t]);
t--;
}
y = fa[x];
int z = fa[y];
while(no_root(x)) {
if(no_root(y)) {
(s[z][] == y) ^ (s[y][] == x) ?
rotate(x) : rotate(y);
}
rotate(x);
y = fa[x];
z = fa[y];
}
return;
} inline void access(int x) {
int y = ;
while(x) {
splay(x);
s[x][] = y;
pushup(x);
y = x;
x = fa[x];
}
return;
} inline int findroot(int x) {
access(x);
splay(x);
pushdown(x);
while(s[x][]) {
x = s[x][];
pushdown(x);
}
return x;
} inline void makeroot(int x) {
access(x);
splay(x);
rev[x] = ;
return;
} inline void link(int x, int y) {
makeroot(x);
if(findroot(y) != x) {
fa[x] = y;
}
return;
} inline void cut(int x, int y) {
makeroot(x);
access(y);
splay(y);
if(s[y][] == x && fa[x] == y && s[x][] == ) {
fa[x] = ;
s[y][] = ;
pushup(y);
}
return;
} inline int getmax(int x, int y) {
makeroot(x);
access(y);
splay(y);
return large[y];
}
}lct; int main() {
int n, m, ans = INF;
scanf("%d%d", &n, &m);
for(int i = ; i <= m; i++) {
read(edge[i].u);
read(edge[i].v);
read(edge[i].a);
read(edge[i].b);
}
std::sort(edge + , edge + m + );
for(int i = ; i <= m; i++) {
lct.val[i + n] = edge[i].b;
} for(int i = ; i <= m; i++) {
int x = edge[i].u;
int y = edge[i].v;
//printf("%d - %d a = %d b = %d \n", x, y, edge[i].a, edge[i].b);
if(ufs.check(x, y)) {
int t = lct.getmax(x, y);
if(lct.val[t] > edge[i].b) {
lct.cut(edge[t - n].u, t);
lct.cut(edge[t - n].v, t);
lct.link(x, i + n);
lct.link(y, i + n);
if(ufs.check(, n)) {
ans = std::min(ans, edge[i].a + lct.val[lct.getmax(, n)]);
}
//printf("1 : ans = %d \n", ans);
}
}
else {
ufs.merge(x, y);
lct.link(x, i + n);
lct.link(y, i + n);
if(ufs.check(, n)) {
ans = std::min(ans, edge[i].a + lct.val[lct.getmax(, n)]);
//printf("2 : ans = %d \n", ans);
}
}
}
printf("%d", ans == INF ? - : ans);
return ;
}

AC代码

update:前面15分是搜索,50分是枚举一条边,暴力维护生成树。70分是枚举a的取值,并查集维护。看代码吧。

 #include <bits/stdc++.h>

 const int N = , INF = 0x3f3f3f3f;

 struct Edge {
int nex, v, a, b, pre;
}edge[N << ]; int tp = ; struct Node {
int a, b, x, y;
inline bool operator < (const Node &w) const {
return a < w.a;
}
}node[N << ]; int e[N], n, m;
std::multiset<int> st; inline void add(int x, int y, int a, int b) {
tp++;
edge[tp].v = y;
edge[tp].a = a;
edge[tp].b = b;
edge[tp].nex = e[x];
if(e[x]) edge[e[x]].pre = tp;
e[x] = tp;
return;
} inline void del(int x, int i) {
if(!edge[i].nex && e[x] == i) {
e[x] = ;
}
else if(!edge[i].nex) {
edge[edge[i].pre].nex = ;
}
else if(e[x] == i) {
e[x] = edge[i].nex;
edge[e[x]].pre = ;
}
else {
edge[edge[i].pre].nex = edge[i].nex;
edge[edge[i].nex].pre = edge[i].pre;
}
return;
} inline void Link() {
for(int i = ; i <= m; i++) {
add(node[i].x, node[i].y, node[i].a, node[i].b);
add(node[i].y, node[i].x, node[i].a, node[i].b);
}
return;
} namespace bf {
int ans = INF;
bool vis[N];
void DFS(int x, int a, int b) {
if(x == n) {
ans = std::min(ans, a + b);
return;
}
vis[x] = ;
for(int i = e[x]; i; i = edge[i].nex) {
int y = edge[i].v;
if(vis[y]) continue;
DFS(y, std::max(a, edge[i].a), std::max(b, edge[i].b));
}
vis[x] = ;
return;
}
inline void solve() {
Link();
DFS(, , );
if(ans == INF) ans = -;
printf("%d\n", ans);
return;
}
} namespace ufs {
int fa[N];
inline void init() {
for(int i = ; i <= n; i++) fa[i] = i;
return;
}
int find(int x) {
if(fa[x] == x) return x;
return fa[x] = find(fa[x]);
}
inline void merge(int x, int y) {
fa[find(x)] = find(y);
return;
}
inline bool check(int x, int y) {
return find(x) == find(y);
}
} namespace bf2 {
int Time, large, pos, vis[N];
bool DFS(int x, int t) {
if(x == t) return true;
vis[x] = Time;
for(int i = e[x]; i; i = edge[i].nex) {
int y = edge[i].v;
if(vis[y] == Time) continue;
if(DFS(y, t)) {
if(large < edge[i].b) {
large = edge[i].b;
pos = i;
}
return true;
}
}
return false;
}
int getMax(int x) {
if(x == n) return ;
vis[x] = Time;
for(int i = e[x]; i; i = edge[i].nex) {
int y = edge[i].v;
if(vis[y] == Time) continue;
int t = getMax(y);
if(t == -) continue;
return std::max(t, edge[i].b);
}
return -;
}
inline void solve() {
int ans = INF;
ufs::init();
for(int i = ; i <= m; i++) {
int x = node[i].x, y = node[i].y;
if(ufs::check(x, y)) {
large = -;
++Time;
DFS(x, y);
if(large <= node[i].b) continue;
del(edge[pos ^ ].v, pos);
del(edge[pos].v, pos ^ );
add(x, y, node[i].a, node[i].b);
add(y, x, node[i].a, node[i].b);
}
else {
ufs::merge(x, y);
add(x, y, node[i].a, node[i].b);
add(y, x, node[i].a, node[i].b);
}
if(ufs::check(, n)) {
++Time;
int temp = getMax();
ans = std::min(ans, node[i].a + temp);
}
}
if(ans == INF) ans = -;
printf("%d\n", ans);
return;
}
} namespace bf3 {
inline bool cmp(const Node &x, const Node &y) {
return x.b < y.b;
}
inline void solve() {
std::sort(node + , node + m + , cmp);
int ans = INF;
for(int lim = ; lim <= ; lim++) {
ufs::init();
int temp = INF;
for(int i = ; i <= m; i++) {
if(node[i].a > lim) {
continue;
}
ufs::merge(node[i].x, node[i].y);
if(ufs::check(, n)) {
temp = node[i].b;
break;
}
}
ans = std::min(ans, lim + temp);
}
if(ans == INF) ans = -;
printf("%d\n", ans);
return;
}
} namespace lct {
int fa[N], s[N][], large[N], val[N], stk[N], top;
bool rev[N];
inline bool no_root(int x) {
return s[fa[x]][] == x || s[fa[x]][] == x;
}
inline void pushup(int x) {
large[x] = x;
if(s[x][] && val[large[x]] < val[large[s[x][]]]) {
large[x] = large[s[x][]];
}
if(s[x][] && val[large[x]] < val[large[s[x][]]]) {
large[x] = large[s[x][]];
}
return;
}
inline void pushdown(int x) {
if(rev[x]) {
if(s[x][]) rev[s[x][]] ^= ;
if(s[x][]) rev[s[x][]] ^= ;
std::swap(s[x][], s[x][]);
rev[x] = ;
}
return;
}
inline void rotate(int x) {
int y = fa[x];
int z = fa[y];
bool f = (s[y][] == x); fa[x] = z;
if(no_root(y)) {
s[z][s[z][] == y] = x;
}
s[y][f] = s[x][!f];
if(s[x][!f]) {
fa[s[x][!f]] = y;
}
s[x][!f] = y;
fa[y] = x; pushup(y);
return;
}
inline void splay(int x) {
int y = x;
stk[top = ] = y;
while(no_root(y)) {
y = fa[y];
stk[++top] = y;
}
while(top) {
pushdown(stk[top]);
top--;
} y = fa[x];
int z = fa[y];
while(no_root(x)) {
if(no_root(y)) {
(s[z][] == y) ^ (s[y][] == x) ?
rotate(x) : rotate(y);
}
rotate(x);
y = fa[x];
z = fa[y];
}
pushup(x);
return;
}
inline void access(int x) {
int y = ;
while(x) {
splay(x);
s[x][] = y;
pushup(x);
y = x;
x = fa[x];
}
return;
}
inline void make_root(int x) {
access(x);
splay(x);
rev[x] ^= ;
return;
}
inline int find_root(int x) {
access(x);
splay(x);
while(s[x][]) {
x = s[x][];
pushdown(x);
}
splay(x);
return x;
}
inline void link(int x, int y) {
make_root(x);
fa[x] = y;
return;
}
inline void cut(int x, int y) {
make_root(x);
access(y);
splay(y);
fa[x] = s[y][] = ;
pushup(y);
return;
}
inline int ask(int x, int y) {
make_root(x);
access(y);
splay(y);
return large[y];
}
} int main() {
scanf("%d%d", &n, &m);
int largeA = -;
for(int i = ; i <= m; i++) {
scanf("%d%d%d%d", &node[i].x, &node[i].y, &node[i].a, &node[i].b);
largeA = std::max(largeA, node[i].a);
}
std::sort(node + , node + m + );
if(n <= && m <= ) {
bf::solve();
return ;
}
if(n <= && m <= ) {
bf2::solve();
return ;
}
if(largeA <= ) {
bf3::solve();
return ;
}
/// lct solve
int ans = INF;
ufs::init();
for(int i = ; i <= m; i++) {
lct::val[n + i] = node[i].b;
}
for(int i = ; i <= m; i++) {
int x = node[i].x, y = node[i].y;
if(ufs::check(x, y)){
int t = lct::ask(x, y);
if(lct::val[t] <= node[i].b) continue;
lct::cut(t, node[t - n].x);
lct::cut(t, node[t - n].y);
lct::link(x, n + i);
lct::link(y, n + i);
}
else {
lct::link(x, n + i);
lct::link(y, n + i);
ufs::merge(x, y);
}
if(ufs::check(, n)) {
ans = std::min(ans, node[i].a + lct::val[lct::ask(, n)]);
}
}
if(ans == INF) ans = -;
printf("%d\n", ans);
return ;
}

AC代码

LOJ#2245 魔法森林的更多相关文章

  1. loj2245 [NOI2014]魔法森林 LCT

    [NOI2014]魔法森林 链接 loj 思路 a排序,b做动态最小生成树. 把边拆成点就可以了. uoj98.也许lct复杂度写假了..越卡常,越慢 代码 #include <bits/std ...

  2. 【BZOJ3669】[Noi2014]魔法森林 LCT

    终于不是裸的LCT了...然而一开始一眼看上去这是kruskal..不对,题目要求1->n的路径上的每个点的两个最大权值和最小,这样便可以用LCT来维护一个最小生成路(瞎编的...),先以a为关 ...

  3. BZOJ 3669 【NOI2014】 魔法森林

    Description 为了得到书法大家的真传,小E同学下定决心去拜访住在魔法森林中的隐士.魔法森林可以被看成一个包含个N节点M条边的无向图,节点标号为1..N,边标号为1..M.初始时小E同学在号节 ...

  4. BZOJ-3669 魔法森林 Link-Cut-Tree

    意识到背模版的重要性了,记住了原理和操作,然后手打模版残了..颓我时间...... 3669: [Noi2014]魔法森林 Time Limit: 30 Sec Memory Limit: 512 M ...

  5. 【BZOJ】3669: [Noi2014]魔法森林(lct+特殊的技巧)

    http://www.lydsy.com/JudgeOnline/problem.php?id=3669 首先看到题目应该可以得到我们要最小化 min{ max{a(u, v)} + max{b(u, ...

  6. NOI2014 魔法森林

    3669: [Noi2014]魔法森林 Time Limit: 30 Sec  Memory Limit: 512 MBSubmit: 106  Solved: 62[Submit][Status] ...

  7. bzoj 3669: [Noi2014]魔法森林 动态树

    3669: [Noi2014]魔法森林 Time Limit: 30 Sec  Memory Limit: 512 MBSubmit: 363  Solved: 202[Submit][Status] ...

  8. 图论 BZOJ 3669 [Noi2014]魔法森林

    Description 为了得到书法大家的真传,小E同学下定决心去拜访住在魔法森林中的隐士.魔法森林可以被看成一个包含个N节点M条边的无向图,节点标号为1..N,边标号为1..M.初始时小E同学在号节 ...

  9. BZOJ 3669: [Noi2014]魔法森林( LCT )

    排序搞掉一维, 然后就用LCT维护加边MST. O(NlogN) ------------------------------------------------------------------- ...

随机推荐

  1. 你不知道的JavaScript——第二章:this全面解析

    1调用位置 调用栈:为了到达当前执行位置所调用的所有函数. function baz(){ //当前调用栈:baz //因此,当前调用位置是全局作用域 console.log('baz'); bar( ...

  2. CSS实现元素水平垂直居中

    我们知道,实现元素的水平居中比较简单,在设置了宽度后,设置左右margin为auto就可以. 但是如何设置元素垂直居中呢? 当然,对于单行的文字,可以通过设置line-height来解决, 可以对于一 ...

  3. saltstack二

    配置管理 haproxy的安装部署 haproxy各版本安装包下载路径https://www.haproxy.org/download/1.6/src/,跳转地址为http,改为https即可 创建相 ...

  4. JQ 动态修改/替换某个节点的内容

     <div class="box">我们定位于中国心理行业第一<div> $(".box").html($(".box&quo ...

  5. LODOP设置判断后执行哪个

    LODOP的语句是普通的语句,可以通过JS判断确定要执行哪个,或通过循环循环执行一些语句.如果需要执行某些打印项在哪些条件下不打印,不需要通过代码删除打印项,类似LODOP.SET_PRINT_STY ...

  6. ExportHandler.ashx

    using KYZWeb.Common;using Liger.Data;//using Microsoft.Office.Interop.Excel;using System;using Syste ...

  7. Visual Studio 2017 and Swagger: Building and Documenting Web APIs

    Swagger是一种与技术无关的标准,允许发现REST API,为任何软件提供了一种识别REST API功能的方法. 这比看起来更重要:这是一个改变游戏技术的方式,就像Web服务描述语言一样WSDL( ...

  8. 11/1/2018模拟 Max

    题面 也就是说, 随机序列RMQ.(\(n \le 8388608\), \(m \le 8*10^6\)) 解法 我写了笛卡尔树+tarjan 然而听神仙说, 因为数据随机, 建完树暴力找lca就行 ...

  9. python3,打印一年的某一天是一年的第几天

    year = int(input('year:')) month = int(input('month:')) day = int(input('day:')) months = (0,31,59,9 ...

  10. 卢卡斯定理&扩展卢卡斯定理

    卢卡斯定理 求\(C_m^n~mod~p\) 设\(m={a_0}^{p_0}+{a_1}^{p_1}+\cdots+{a_k}^{p_k},n={b_0}^{p_0}+{b_1}^{p_1}+\cd ...