http://uoj.ac/problem/3 (题目链接)

题意

  给出一张带权图,每条边有两个权值A和B,一条路径的花费为路径中的最大的A和最大的B之和。求从1走到n的最小花费。

Solution

  枚举A,SPFA松弛。

  不得不说UOJ的hack还是很强力的,仔细想了想,数据好像也并不是特别难构。只要使每次加边都必须遍历整张图,之前n都未连通,最后一条A最大的边连向n即可。

代码

// uoj3
#include<algorithm>
#include<iostream>
#include<cstdlib>
#include<cstring>
#include<cstdio>
#include<vector>
#include<cmath>
#include<queue>
#include<ctime>
#define LL long long
#define inf (1ll<<30)
#define Pi acos(-1.0)
#define free(a) freopen(a".in","r",stdin),freopen(a".out","w",stdout);
using namespace std; const int maxn=50010,maxm=100010;
int head[maxn],dis[maxn],fa[maxn],vis[maxn],n,m,cnt;
struct data {int u,v,wa,wb;}a[maxm];
struct edge {int to,next,wa,wb;}e[maxm<<1]; void link(int u,int v,int wa,int wb) {
e[++cnt]=(edge){v,head[u],wa,wb};head[u]=cnt;
e[++cnt]=(edge){u,head[v],wa,wb};head[v]=cnt;
}
int find(int x) {
return x==fa[x] ? x : fa[x]=find(fa[x]);
}
bool cmp(data a,data b) {
return a.wa<b.wa;
}
int main() {
scanf("%d%d",&n,&m);
for (int i=1;i<=n;i++) fa[i]=i;
for (int i=1;i<=m;i++) {
scanf("%d%d%d%d",&a[i].u,&a[i].v,&a[i].wa,&a[i].wb);
link(a[i].u,a[i].v,a[i].wa,a[i].wb);
if (find(a[i].u)!=find(a[i].v)) fa[find(a[i].u)]=find(a[i].v);
}
if (find(1)!=find(n)) {puts("-1");return 0;}
sort(a+1,a+1+m,cmp);
for (int i=1;i<=n;i++) dis[i]=inf;dis[1]=0;
int ans=inf;vis[n]=1;
for (int i=1;i<=m;i++) {
queue<int> q;
if (!vis[a[i].u]) q.push(a[i].u),vis[a[i].u]=1;
if (!vis[a[i].v]) q.push(a[i].v),vis[a[i].v]=1;
while (i<m && a[i+1].wa==a[i].wa) {
i++;
if (!vis[a[i].u]) q.push(a[i].u),vis[a[i].u]=1;
if (!vis[a[i].v]) q.push(a[i].v),vis[a[i].v]=1;
}
while (!q.empty()) {
int x=q.front();q.pop();
vis[x]=0;
for (int j=head[x];j;j=e[j].next)
if (e[j].wa<=a[i].wa && dis[e[j].to]>max(dis[x],e[j].wb)) {
dis[e[j].to]=max(dis[x],e[j].wb);
if (!vis[e[j].to]) vis[e[j].to]=1,q.push(e[j].to);
}
}
ans=min(ans,a[i].wa+dis[n]);
if ((double)clock()/CLOCKS_PER_SEC>=2.8) break;
}
printf("%d\n",ans);
return 0;
}

Solution

  link cut tree维护最小生成树。把边权转为点权表示。每次加边时,如果${u,v}$在一个连通块内,查询连通块中点权最大值,如果大于当前边的边权,就把这个点删掉。每次加完一条边后,如果起点和终点在同一连通块中,就更新下答案。

代码

// uoj2
#include<algorithm>
#include<iostream>
#include<cstdlib>
#include<cstring>
#include<cstdio>
#include<cmath>
#define LL long long
#define inf (1ll<<30)
#define Pi acos(-1.0)
#define free(a) freopen(a".in","r",stdin),freopen(a".out","w",stdout);
using namespace std; const int maxn=200010;
int a[maxn],tr[maxn][2],rev[maxn],fa[maxn],mx[maxn];
int n,m; struct edge {
int u,v,wa,wb;
friend bool operator < (const edge a,const edge b) {
return a.wa<b.wa;
}
}e[maxn]; void pushup(int x) {
if (a[mx[tr[x][0]]]>a[mx[tr[x][1]]]) mx[x]=mx[tr[x][0]];
else mx[x]=mx[tr[x][1]];
if (a[mx[x]]<a[x]) mx[x]=x;
}
void pushdown(int x) {
if (tr[fa[x]][0]==x || tr[fa[x]][1]==x) pushdown(fa[x]);
if (rev[x]) {
swap(tr[x][0],tr[x][1]);
rev[tr[x][0]]^=1;rev[tr[x][1]]^=1;rev[x]^=1;
}
}
void rotate(int x) {
int y=fa[x],z=fa[y],l,r;
l=tr[y][1]==x;r=l^1;
if (tr[z][0]==y || tr[z][1]==y) tr[z][tr[z][1]==y]=x;
fa[y]=x;fa[x]=z;fa[tr[x][r]]=y;
tr[y][l]=tr[x][r];tr[x][r]=y;
pushup(y);pushup(x);
}
void splay(int x) {
pushdown(x);
while (tr[fa[x]][0]==x || tr[fa[x]][1]==x) {
int y=fa[x],z=fa[y];
if (tr[z][0]==y || tr[z][1]==y) {
if (tr[z][0]==y ^ tr[y][0]==x) rotate(x);
else rotate(y);
}
rotate(x);
}
}
void access(int x) {
for (int y=0;x;y=x,x=fa[x])
splay(x),tr[x][1]=y,pushup(x);
}
void makeroot(int x) {
access(x);splay(x);rev[x]^=1;
}
void link(int x,int y) {
makeroot(x);fa[x]=y;
}
void cut(int x,int y) {
makeroot(x);access(y);splay(y);
tr[y][0]=fa[x]=0;pushup(y);
}
int find(int x) {
while (fa[x]) x=fa[x];
return x;
}
int query(int x,int y) {
makeroot(x);access(y);splay(y);
return mx[y];
}
int main() {
scanf("%d%d",&n,&m);
for (int i=1;i<=m;i++) scanf("%d%d%d%d",&e[i].u,&e[i].v,&e[i].wa,&e[i].wb);
sort(e+1,e+1+m);
int ans=inf;
for (int i=1;i<=n;i++) a[i]=0;
for (int i=1;i<=m;i++) {
a[i+n]=e[i].wb;
if (find(e[i].u)!=find(e[i].v)) link(e[i].u,i+n),link(e[i].v,i+n);
else {
int tmp=query(e[i].u,e[i].v);
if (a[tmp]>a[i+n]) {
cut(e[tmp-n].u,tmp),cut(e[tmp-n].v,tmp);
link(e[i].u,i+n),link(e[i].v,i+n);
}
}
if (find(1)==find(n)) ans=min(ans,e[i].wa+a[query(1,n)]);
}
printf("%d",ans==inf ? -1 : ans);
return 0;
}

  

【uoj3】 NOI2014—魔法森林的更多相关文章

  1. NOI2014 魔法森林

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

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

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

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

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

  4. bzoj 3669: [Noi2014]魔法森林

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

  5. BZOJ_3669_[Noi2014]魔法森林_LCT

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

  6. bzoj 3669: [Noi2014]魔法森林 (LCT)

    链接:https://www.lydsy.com/JudgeOnline/problem.php?id=3669 题面: 3669: [Noi2014]魔法森林 Time Limit: 30 Sec  ...

  7. 「luogu2387」[NOI2014] 魔法森林

    「luogu2387」[NOI2014] 魔法森林 题目大意 \(n\) 个点 \(m\) 条边的无向图,每条边上有两个权值 \(a,b\),求从 \(1\) 节点到 \(n\) 节点 \(max\{ ...

  8. P2387 [NOI2014]魔法森林(LCT)

    P2387 [NOI2014]魔法森林 LCT边权维护经典题 咋维护呢?边化为点,边权变点权. 本题中我们把边对关键字A进行排序,动态维护关键字B的最小生成树 加边后出现环咋办? splay维护最大边 ...

  9. [NOI2014]魔法森林 LCT

    题面 [NOI2014]魔法森林 题解 一条路径的代价为路径上的\(max(a[i]) + max(b[i])\),因为一条边同时有$a[i], b[i]$2种权值,直接处理不好同时兼顾到,所以我们考 ...

  10. bzoj 3669: [Noi2014]魔法森林 -- 动点spfa

    3669: [Noi2014]魔法森林 Time Limit: 30 Sec  Memory Limit: 512 MB 动点spfa Description 为了得到书法大家的真传,小E同学下定决心 ...

随机推荐

  1. 20155227《网络对抗》Exp6 信息收集与漏洞扫描

    20155227<网络对抗>Exp6 信息收集与漏洞扫描 实践目标 掌握信息搜集的最基础技能与常用工具的使用方法. 基础问题回答 哪些组织负责DNS,IP的管理. 全球根服务器均由美国政府 ...

  2. 【个人】爬虫实践,利用xpath方式爬取数据之爬取虾米音乐排行榜

    实验网站:虾米音乐排行榜 网站地址:http://www.xiami.com/chart  难度系数:★☆☆☆☆ 依赖库:request.lxml的etree (安装lxml:pip install ...

  3. HTML基础之JS

    HTML中的三把利器的JS 又称为JavaScript,看着好像和Java有点联系,实际上他和java半毛钱关系都没有,JavaScript和我们学习的Python.Go.Java.C++等,都是一种 ...

  4. Redux系列x:源码解析

    写在前面 redux的源码很简洁,除了applyMiddleware比较绕难以理解外,大部分还是 这里假设读者对redux有一定了解,就不科普redux的概念和API啥的啦,这部分建议直接看官方文档. ...

  5. 如何在内网安装compass

    神器compass是肿么用这里不做介绍,因为我也不清楚,可参考官网:http://compass-style.org.这里主要介绍如何在内网安装compass. 首先介绍一般是如何安装compass的 ...

  6. kubernetes 集群新增node 节点并将应用分配到新增节点

    第一章 1.重新安装一台kubernetes node节点,新增节点:192.168.1.192 网址:https://www.cnblogs.com/zoulixiang/p/9504324.htm ...

  7. NO.3:自学tensorflow之路------MNIST识别,神经网络拓展

    引言 最近自学GRU神经网络,感觉真的不简单.为了能够快速跑完程序,给我的渣渣笔记本(GT650M)也安装了一个GPU版的tensorflow.顺便也更新了版本到了tensorflow-gpu 1.7 ...

  8. 20135316Linux内核学习笔记第五周

    20135316王剑桥<Linux内核分析>MOOC课程http://mooc.study.163.com/course/USTC-1000029000 一.给MenuOS增加time和t ...

  9. Linux内核读书笔记第二周

    什么是系统调用 简单来说,系统调用就是用户程序和硬件设备之间的桥梁.用户程序在需要的时候,通过系统调用来使用硬件设备. 系统调用的存在,有以下重要的意义: 1)用户程序通过系统调用来使用硬件,而不用关 ...

  10. c++实现计算器功能 -----初代

    由于时间问题,我就写的简单一点. 课程作业一 git链接: Operations 里面的Operations.cpp文件就是完成品. 1 我就简单的对我原来的代码进行了重构,原本的代码已经把函数都分得 ...