这道题以前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. childNodes遍历DOM节点树

    childNodes遍历DOM节点树 var s = ""; function travel(space,node) { if(node.tagName){ s += space ...

  2. CLOUD流程设置

    流程-反写规则 允许超额

  3. shell中数组及其相关操作

    转载 https://blog.csdn.net/jerry_1126/article/details/52027539

  4. ps -aux返回超时的可能原因

    在我的环境上出现了 ps -aux返回超时的问题. 执行到 ……root 19342 0.0 0.0 0 0 ? S< Mar12 0:00 [kworker/34:1H] 这里,然后就卡住了. ...

  5. ERP行业内幕看了这五个问题全懂了

    ERP系统是现代企业实现信息化管理的必经之路.但很多管理人员或已经在用ERP的人员,其实并不太懂ERP系统是什么意思,有哪些好处等,导致实际使用过程中经常大材小用,或者“英雄无用武之地”.所以,为了更 ...

  6. Draw your Next App Idea with Ink to Code

    Imagine that you’ve just been struck by inspiration for your next great app. You might start by jott ...

  7. JSON in SQL Server 2016

    JSON functions in SQL Server enable you to analyze and query JSON data, transform JSON to relational ...

  8. c++数字和字符串的转换

    1  利用stringstream 添加头文件 #include<sstream> 数字转字符串 #include <string>   #include <sstrea ...

  9. codeforces732C

    Sanatorium CodeForces - 732C Vasiliy spent his vacation in a sanatorium, came back and found that he ...

  10. BZOJ3876[Ahoi2014&Jsoi2014]支线剧情——有上下界的最小费用最大流

    题目描述 [故事背景] 宅男JYY非常喜欢玩RPG游戏,比如仙剑,轩辕剑等等.不过JYY喜欢的并不是战斗场景,而是类似电视剧一般的充满恩怨情仇的剧情.这些游戏往往 都有很多的支线剧情,现在JYY想花费 ...