BZOJ 3669: [Noi2014]魔法森林 [LCT Kruskal | SPFA]
题目描述
为了得到书法大家的真传,小 E 同学下定决心去拜访住在魔法森林中的隐 士。魔法森林可以被看成一个包含 n 个节点 m 条边的无向图,节点标号为 1,2,3,…,n,边标号为 1,2,3,…,m。初始时小 E 同学在 1 号节点,隐士则住在 n 号节点。小 E 需要通过这一片魔法森林,才能够拜访到隐士。
魔法森林中居住了一些妖怪。每当有人经过一条边的时候,这条边上的妖怪 就会对其发起攻击。幸运的是,在 1 号节点住着两种守护精灵:A 型守护精灵与 B 型守护精灵。小 E 可以借助它们的力量,达到自己的目的。
只要小 E 带上足够多的守护精灵,妖怪们就不会发起攻击了。具体来说,无 向图中的每一条边 ei 包含两个权值 ai 与 bi 。若身上携带的 A 型守护精灵个数不 少于 ai ,且 B 型守护精灵个数不少于 bi ,这条边上的妖怪就不会对通过这条边 的人发起攻击。当且仅当通过这片魔法森林的过程中没有任意一条边的妖怪向 小 E 发起攻击,他才能成功找到隐士。
由于携带守护精灵是一件非常麻烦的事,小 E 想要知道,要能够成功拜访到 隐士,最少需要携带守护精灵的总个数。守护精灵的总个数为 A 型守护精灵的 个数与 B 型守护精灵的个数之和。
输入输出格式
输入格式:
输入文件的第 1 行包含两个整数 n,m,表示无向图共有 n 个节点,m 条边。 接下来 m 行,第i+ 1 行包含 4 个正整数 Xi,Yi,ai,bi,描述第i条无向边。 其中Xi与 Yi为该边两个端点的标号,ai 与 bi 的含义如题所述。 注意数据中可能包含重边与自环。
输出格式:
输出一行一个整数:如果小 E 可以成功拜访到隐士,输出小 E 最少需要携 带的守护精灵的总个数;如果无论如何小 E 都无法拜访到隐士,输出“-1”(不 含引号)。
输入输出样例
4 5
1 2 19 1
2 3 8 12
2 4 12 15
1 3 17 8
3 4 1 17
32
3 1
1 2 1 1
-1
考虑两个权值的MST?
a排序后b无序啊,LCT是可以维护动态加边的MST的啊
这里不会有连通块分开,所以判连通在用个并查集能快一点 还有一种做法,动态加边SPFA,a排序每次加入边权值为b和边的两个端点到队列。。。。。快了1倍多
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
using namespace std;
#define pa t[x].fa
#define lc t[x].ch[0]
#define rc t[x].ch[1]
const int N=15e4+,M=1e5+,INF=1e9;
typedef long long ll;
inline int read(){
char c=getchar();int x=,f=;
while(c<''||c>''){if(c=='-')f=-;c=getchar();}
while(c>=''&&c<=''){x=x*+c-'';c=getchar();}
return x*f;
} int n,m,Q,type;
struct LCTnode{
int ch[],fa,rev,w,mx,p;
}t[N];
inline int wh(int x){return t[pa].ch[]==x;}
inline int isRoot(int x){return t[pa].ch[]!=x&&t[pa].ch[]!=x;}
inline void update(int x){
t[x].p=x;t[x].mx=t[x].w;
if(t[lc].mx>t[x].mx) t[x].mx=t[lc].mx,t[x].p=t[lc].p;
if(t[rc].mx>t[x].mx) t[x].mx=t[rc].mx,t[x].p=t[rc].p;
}
inline void rever(int x){
t[x].rev^=;
swap(lc,rc);
}
inline void pushDown(int x){
if(t[x].rev){
rever(lc);
rever(rc);
t[x].rev=;
}
}
inline void rotate(int x){
int f=t[x].fa,g=t[f].fa,c=wh(x);
if(!isRoot(f)) t[g].ch[wh(f)]=x;t[x].fa=g;
t[f].ch[c]=t[x].ch[c^];t[t[f].ch[c]].fa=f;
t[x].ch[c^]=f;t[f].fa=x;
update(f);update(x);
}
int st[N],top;
inline void splay(int x){
top=;st[++top]=x;
for(int i=x;!isRoot(i);i=t[i].fa) st[++top]=t[i].fa;
for(int i=top;i>=;i--) pushDown(st[i]); for(;!isRoot(x);rotate(x))
if(!isRoot(pa)) rotate(wh(x)==wh(pa)?pa:x);
}
inline void Access(int x){
for(int y=;x;y=x,x=pa){
splay(x);
rc=y;
update(x);
}
}
inline void MakeR(int x){
Access(x);splay(x);
rever(x);
}
inline int FindR(int x){
Access(x);splay(x);
while(lc) x=lc;
return x;
}
inline void Link(int x,int y){
MakeR(x);
t[x].fa=y;
}
inline void Cut(int x,int y){
MakeR(x);Access(y);splay(y);
t[y].ch[]=t[x].fa=;
update(y);
}
inline int Que(int x,int y){
MakeR(x);Access(y);splay(y);
return t[y].p;
} struct edge{
int u,v,a,b;
bool operator <(const edge &r)const{return a<r.a;}
}e[M];
int ans=INF;
int fa[N];
inline int find(int x){return x==fa[x]?x:fa[x]=find(fa[x]);}
void Kruskal(){
for(int i=;i<=n;i++) fa[i]=i;
sort(e+,e++m);
for(int i=;i<=m;i++){
int u=e[i].u,v=e[i].v;
if(find(u)!=find(v)){
fa[find(u)]=find(v);
Link(u,i+n),Link(v,i+n);
t[i+n].w=t[i+n].mx=e[i].b;
t[i+n].p=i+n;
}else{
int p=Que(u,v);
if(t[p].w>e[i].b){
Cut(e[p-n].u,p),Cut(e[p-n].v,p);
Link(u,i+n),Link(v,i+n);
t[i+n].w=t[i+n].mx=e[i].b;
t[i+n].p=i+n;
}
}
if(find()==find(n)) ans=min(ans,e[i].a+t[Que(,n)].w);
}
} int main(){
//freopen("in.txt","r",stdin);
n=read();m=read();
for(int i=;i<=m;i++) e[i].u=read(),e[i].v=read(),e[i].a=read(),e[i].b=read();
Kruskal();
printf("%d",ans==INF?-:ans);
}
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
using namespace std;
const int N=5e4+,M=1e5+,INF=1e9;
typedef long long ll;
inline int read(){
char c=getchar();int x=,f=;
while(c<''||c>''){if(c=='-')f=-;c=getchar();}
while(c>=''&&c<=''){x=x*+c-'';c=getchar();}
return x*f;
}
int n,m;
struct data{
int u,v,a,b;
bool operator <(const data &r)const{return a<r.a;}
}a[M];
struct edge{
int v,ne,w;
}e[M<<];
int cnt,h[N];
inline void ins(int u,int v,int w){
cnt++;
e[cnt].v=v;e[cnt].w=w;e[cnt].ne=h[u];h[u]=cnt;
cnt++;
e[cnt].v=u;e[cnt].w=w;e[cnt].ne=h[v];h[v]=cnt;
}
int d[N],q[N],head,tail,inq[N];
inline void lop(int &x){if(x==) x=N-;else if(x==N) x=;}
inline void push(int v){
if(d[v]<d[q[head]]) lop(--head),q[head]=v;
else q[tail++]=v,lop(tail);
inq[v]=;
}
void spfa(){
while(head!=tail){
int u=q[head++];inq[u]=;lop(head);
for(int i=h[u];i;i=e[i].ne){
int v=e[i].v,w=e[i].w;
if(d[v]>max(d[u],w)){
d[v]=max(d[u],w);
if(!inq[v]) push(v);
}
}
}
}
int ans=INF;
void solve(){
sort(a+,a++m);
head=tail=;
memset(d,0x3f,sizeof(d));
q[tail++]=;d[]=;inq[]=;
for(int i=;i<=m;i++){
ins(a[i].u,a[i].v,a[i].b);
head=tail=;
q[tail++]=a[i].u;inq[a[i].u]=;
q[tail++]=a[i].v;inq[a[i].v]=;
spfa();
ans=min(ans,a[i].a+d[n]);
}
}
int main(int argc, const char * argv[]) {
n=read();m=read();
for(int i=;i<=m;i++) a[i].u=read(),a[i].v=read(),a[i].a=read(),a[i].b=read();
solve();
printf("%d",ans==INF?-:ans);
}
BZOJ 3669: [Noi2014]魔法森林 [LCT Kruskal | SPFA]的更多相关文章
- BZOJ 3669: [Noi2014]魔法森林( LCT )
排序搞掉一维, 然后就用LCT维护加边MST. O(NlogN) ------------------------------------------------------------------- ...
- bzoj 3669: [Noi2014]魔法森林 (LCT)
链接:https://www.lydsy.com/JudgeOnline/problem.php?id=3669 题面: 3669: [Noi2014]魔法森林 Time Limit: 30 Sec ...
- bzoj 3669: [Noi2014]魔法森林 -- 动点spfa
3669: [Noi2014]魔法森林 Time Limit: 30 Sec Memory Limit: 512 MB 动点spfa Description 为了得到书法大家的真传,小E同学下定决心 ...
- bzoj 3669: [Noi2014] 魔法森林 LCT版
Description 为了得到书法大家的真传,小E同学下定决心去拜访住在魔法森林中的隐士.魔法森林可以被看成一个包含个N节点M条边的无向图,节点标号为1..N,边标号为1..M.初始时小E同学在号节 ...
- BZOJ 3669: [Noi2014]魔法森林(lct+最小生成树)
传送门 解题思路 \(lct\)维护最小生成树.我们首先按照\(a\)排序,然后每次加入一条边,在图中维护一棵最小生成树.用并查集判断一下\(1\)与\(n\)是否联通,如果联通的话就尝试更新答案. ...
- bzoj 3669: [Noi2014]魔法森林
bzoj 3669: [Noi2014]魔法森林 Description 为了得到书法大家的真传,小E同学下定决心去拜访住在魔法森林中的隐士.魔法森林可以被看成一个包含个N节点M条边的无向图,节点标号 ...
- 【BZOJ 3669】 3669: [Noi2014]魔法森林 (动态spfa)
3669: [Noi2014]魔法森林 Description 为了得到书法大家的真传,小E同学下定决心去拜访住在魔法森林中的隐士.魔法森林可以被看成一个包含个N节点M条边的无向图,节点标号为1..N ...
- bzoj 3669: [Noi2014]魔法森林 动态树
3669: [Noi2014]魔法森林 Time Limit: 30 Sec Memory Limit: 512 MBSubmit: 363 Solved: 202[Submit][Status] ...
- [BZOJ 3669] [Noi2014] 魔法森林 【LCT】
题目链接:BZOJ - 3669 题目分析 如果确定了带 x 只精灵A,那么我们就是要找一条 1 到 n 的路径,满足只经过 Ai <= x 的边,而且要使经过的边中最大的 Bi 尽量小. 其实 ...
随机推荐
- 移动App,AJAX异步请求,实现简单的增、删、改、查
用ajax发异步请求时,要注意url."AppServer"为后台项目名,"LoginServlet.action"为web.xml中的<url-patt ...
- mitm6:通过IPv6攻破IPv4网络
一.前言 虽然IPv6正在互联网上逐步推广,但在内部网络环境中使用IPv6的公司依然非常稀少.然而,大多数公司并不知道,即使他们没有主动去使用IPv6,但从Windows Vista以来,所有的Win ...
- Google PageSpeed Tools 性能测试分析
今天给大家介绍下一个工具:Google PageSpeed Tools,根据官方的介绍,简单梳理如下: Page Speed Insights能针对移动设备和电脑设备衡量网页的性能.该工具会抓取网址两 ...
- 人生苦短,我用Python 第一篇
一.变量名 自定义变量名只能用 数字.字母.下划线组成. 使用变量前,需对其先赋值 注意:1.变量名不能以数字开头: 2.不能是关键字:例如:'and', 'as', 'assert', ' ...
- 【django基础之ORM】
一.定义 1.什么是ORM? ORM,即Object-Relational Mapping(对象关系映射),它的作用是在关系型数据库和业务实体对象之间作一个映射,这样,我们在具体的操作业务对象的时候, ...
- 初识RabbitMQ,附RabbitMQ+PHP演示实例
RabbitMQ是一个在AMQP基础上实现的企业级消息系统.何谓消息系统,就是消息队列系统,消息队列是""消费-生产者模型""的一个典型的代表,一端往消息队列中 ...
- // 关闭调试模式 define('APP_DEBUG', false);
调试模式的优势在于: 开启日志记录,任何错误信息和调试信息都会详细记录,便于调试: 关闭模板缓存,模板修改可以即时生效: 记录SQL日志,方便分析SQL: 关闭字段缓存,数据表字段修改不受缓存影响: ...
- Moogoose Constructor小坑
注意! exports 出来的 Model名字,必须和 Constructor的名字不一样!!! 不然Constructor会被覆盖,报错 这个是修改之后的.修改前,是var account = ne ...
- Java 获得Class的绝对路径方法
Java获得class文件的绝对路径:1.e.g. Foo.class => Foo.class.getResuorce("").getFile(); 该方法在eclipse ...
- Linuxc - 执行c程序
1.新建工作空间 root@jiqing-virtual-machine:~/cspace/les1# pwd /root/cspace/les1 2.新建c程序 root@jiqing-virtua ...