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) ------------------------------------------------------------------- ...
随机推荐
- mapreduce join
MapReduce Join 对两份数据data1和data2进行关键词连接是一个很通用的问题,如果数据量比较小,可以在内存中完成连接. 如果数据量比较大,在内存进行连接操会发生OOM.mapredu ...
- python之路--第一类对象,函数名,变量名
一 . 第一类对象 函数对象可以像变量一样进行赋值 , 还可以作为列表的元素进行使用 可以作为返回值返回 , 可以作为参数进行传递 def func(): def people(): print('金 ...
- JAVA不可变类(immutable)机制与String的不可变性--非常好.
JAVA不可变类(immutable)机制与String的不可变性 https://www.cnblogs.com/jaylon/p/5721571.html
- window.location.href ie 不兼容问题
今天再做项目演示的时候,用的是ie浏览器报错404,项目都运行好久了,第一次用ie就这样了悲剧,贴下解决方法吧 function getContextPath() { var pathName = d ...
- WebStorm开发React项目,修代码之后运行的项目不更新
昨天遇到一个恶心的问题,我新建了一个React.js项目,想做一点关于Pc端的开发,习惯性的用了WebStorm,可是发现一个问题,就是代码修改之后,浏览器上根本不会刷新.然后,我把方向定位在没有正确 ...
- 定位linux jdk安装路径
如何在一台Linux服务器上查找JDK的安装路径呢? 有那些方法可以查找定位JDK的安装路径?是否有一些局限性呢? 下面总结了一下如何查找JDK安装路径的方法. 1:echo $JAVA_HOME 使 ...
- Vasya and a Tree CodeForces - 1076E(线段树+dfs)
I - Vasya and a Tree CodeForces - 1076E 其实参考完别人的思路,写完程序交上去,还是没理解啥意思..昨晚再仔细想了想.终于弄明白了(有可能不对 题意是有一棵树n个 ...
- poj2100(尺取法)
题意:选取一系列数,使得这些数的平方和等于n: 解题思路:尺取法扫一遍: #include<iostream> #include<algorithm> using namesp ...
- servlet篇 之servlet实现
一:如何写一个servlet 实现/继承 如下 接口/类 Servlet 接口 有五个抽象方法 GenericServlet 抽象类 有一个抽象方法 HttpServlet 抽象类 ...
- Ubuntu18.04下安装Sublime Text3!
这几天安装了Ubuntu18.04,然后在里面安装Sublime Text3,结果各种问题!各种BUG!试了网上各种办法!尼玛!都是坑爹的啊! 最后还是楼主自己解决了…… 废话不多说,直接按顺序执行下 ...