这道题以前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. 不停机修改线上 MySQL 主键字段 以及其带来的问题和总结思考

    起因: 线上 user 数据库没有自增字段,数据量已经达到百万级.无论是给离线仓库还是数据分析同步数据,没有主键自增 id 都是杀手级的困难.所以在使用 create_time 痛苦了几次之后准备彻底 ...

  2. xadmin快速搭建后台管理系统

    一.xadmin的特点: 1.基于Bootstrap3:Xadmin使用Bootstrap3.0框架精心打造.基于Bootstrap3,Xadmin天生就支持在多种屏幕上无缝浏览,并完全支持Boots ...

  3. UDP反射DDoS攻击原理和防范

    东南大学:UDP反射DDoS攻击原理和防范 2015-04-17 中国教育网络 李刚 丁伟 反射攻击的防范措施 上述协议安装后由于有关服务默认处于开启状态,是其被利用的一个重要因素.因此,防范可以从配 ...

  4. 数据驱动-参数化(Parameters)

    在录制程序运行的过程中,Vugen(脚本生成器)自动生成了脚本以及录制过程中实际用到的数据.在这个时候,脚本和数据是混在一起的. 在登录操作中,很明显xpj与123123是填入的数据,如果Contro ...

  5. Appium之编写H5应用测试脚本(切换到Webview)

    App使用H5编写,默认方式找不到元素.启动后获取所有上下文,找到webivew_xxxx的,然后进行切换. 源码: package MyAppium; import io.appium.java_c ...

  6. 今天开始学习模式识别与机器学习Pattern Recognition and Machine Learning (PRML),章节5.1,Neural Networks神经网络-前向网络。

    话说上一次写这个笔记是13年的事情了···那时候忙着实习,找工作,毕业什么的就没写下去了,现在工作了有半年时间也算稳定了,我会继续把这个笔记写完.其实很多章节都看了,不过还没写出来,先从第5章开始吧, ...

  7. oracle逗号分隔函数

    SELECT wm_concat(GZTYPE) str FROM TB_FDN_WORKKIND

  8. POJ1151-扫面线+线段树+离散化//入门题

    比较水的入门题 记录矩形竖边的x坐标,离散化排序.以被标记的边建树. 扫描线段树,查询线段树内被标记的边.遇到矩形的右边就删除此边 每一段的面积是查询结果乘边的横坐标之差,求和就是答案 #includ ...

  9. Hdoj 2036.改革春风吹满地 题解

    Problem Description " 改革春风吹满地, 不会AC没关系; 实在不行回老家, 还有一亩三分地. 谢谢!(乐队奏乐)" 话说部分学生心态极好,每天就知道游戏,这次 ...

  10. Hdoj 1253.胜利大逃亡 题解

    Problem Description Ignatius被魔王抓走了,有一天魔王出差去了,这可是Ignatius逃亡的好机会. 魔王住在一个城堡里,城堡是一个ABC的立方体,可以被表示成A个B*C的矩 ...