LOJ#2245 魔法森林
这道题以前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 魔法森林的更多相关文章
- loj2245 [NOI2014]魔法森林 LCT
[NOI2014]魔法森林 链接 loj 思路 a排序,b做动态最小生成树. 把边拆成点就可以了. uoj98.也许lct复杂度写假了..越卡常,越慢 代码 #include <bits/std ...
- 【BZOJ3669】[Noi2014]魔法森林 LCT
终于不是裸的LCT了...然而一开始一眼看上去这是kruskal..不对,题目要求1->n的路径上的每个点的两个最大权值和最小,这样便可以用LCT来维护一个最小生成路(瞎编的...),先以a为关 ...
- BZOJ 3669 【NOI2014】 魔法森林
Description 为了得到书法大家的真传,小E同学下定决心去拜访住在魔法森林中的隐士.魔法森林可以被看成一个包含个N节点M条边的无向图,节点标号为1..N,边标号为1..M.初始时小E同学在号节 ...
- BZOJ-3669 魔法森林 Link-Cut-Tree
意识到背模版的重要性了,记住了原理和操作,然后手打模版残了..颓我时间...... 3669: [Noi2014]魔法森林 Time Limit: 30 Sec Memory Limit: 512 M ...
- 【BZOJ】3669: [Noi2014]魔法森林(lct+特殊的技巧)
http://www.lydsy.com/JudgeOnline/problem.php?id=3669 首先看到题目应该可以得到我们要最小化 min{ max{a(u, v)} + max{b(u, ...
- NOI2014 魔法森林
3669: [Noi2014]魔法森林 Time Limit: 30 Sec Memory Limit: 512 MBSubmit: 106 Solved: 62[Submit][Status] ...
- bzoj 3669: [Noi2014]魔法森林 动态树
3669: [Noi2014]魔法森林 Time Limit: 30 Sec Memory Limit: 512 MBSubmit: 363 Solved: 202[Submit][Status] ...
- 图论 BZOJ 3669 [Noi2014]魔法森林
Description 为了得到书法大家的真传,小E同学下定决心去拜访住在魔法森林中的隐士.魔法森林可以被看成一个包含个N节点M条边的无向图,节点标号为1..N,边标号为1..M.初始时小E同学在号节 ...
- BZOJ 3669: [Noi2014]魔法森林( LCT )
排序搞掉一维, 然后就用LCT维护加边MST. O(NlogN) ------------------------------------------------------------------- ...
随机推荐
- mvc后台可以使前台弹出警告框的几种方法
1,引用博主的 第一种方案:在jsp页面使用java代码实现. 后台处理代码:(把要提示的数据放到session中.) String message = ""; message = ...
- Spring boot 全局配置文件application.properties
#更改Tomcat端口号 server.port=8090 #修改进入DispatcherServlet的规则为:*.htmlserver.servlet-path=*.html#这里要注意高版本的s ...
- servletContext和request对象的生命周期比较
ServletContext: 创建:服务器启动 销毁:服务器关闭 域的作用范围:整个web应用 Request: 创建:访问时创建request 销毁:响应结束request销毁 域的作用范围:一次 ...
- css多列居中
https://jingyan.baidu.com/article/36d6ed1f67d58f1bcf488393.html
- PreparedStement 用户登录!
一.准备工作 在qy66数据库下,新建一个denglu表.添加 name password . package cn.zhouzhou; import java.sql.Connection; im ...
- Python——Flask框架——程序的基本结构
一.安装 pip install flask 二.初始化 from flask import Flask app = Flash(__name__) 三.路由:处理URL和函数之间的关系的程序称为路由 ...
- ES 6 系列 - Promise
一.含义 是异步编程的一种解决方案,es 6 将其变成了标准. 简单的说是一个容器,里面保存了某个未来才会结束的事件(通常是一个异步操作)的结果.语法上, Promise 是一个对象,从它可以获取异步 ...
- Uncaught SyntaxError: Unexpected token export
开发过程中遇到这个错误,虽然不影响使用,但是每次浏览器控制台都会有错误输出,看起来十分不舒服,故翻阅资料发现是因为浏览器虽然支持了es6,但是不支持es6的Module直接使用,需要在script标签 ...
- javadoc格式化,解决多个形参空格暴多,页面溢出问题
格式化前: 格式化后: pom.xml <?xml version="1.0" encoding="UTF-8"?> <project xml ...
- web scraper——安装【一】
准备工作 工欲善其事必先利其器,既然是要安装web scraper一些***的工具是必然不可缺少的,如果没有的话,先下载个蓝灯用用吧. 蓝灯最新版下载地址 下载安装完成后双击打开即可,这时候会弹出一个 ...