bzoj3669&uoj3&luogu2387 [NOI2014]魔法森林

(http://www.elijahqi.win/2018/01/27/bzoj3669uoj3luogu2387-noi2014%E9%AD%94%E6%B3%95%E6%A3%AE%E6%9E%97/%20%E2%80%8E)
题目描述

为了得到书法大家的真传,小 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”(不 含引号)。
输入输出样例

输入样例#1: 复制

4 5
1 2 19 1
2 3 8 12
2 4 12 15
1 3 17 8
3 4 1 17

输出样例#1: 复制

32

输入样例#2: 复制

3 1
1 2 1 1

输出样例#2: 复制

-1

说明

解释1

如果小 E 走路径 1→2→4,需要携带 19+15=34 个守护精灵; 如果小 E 走路径 1→3→4,需要携带 17+17=34 个守护精灵; 如果小 E 走路径 1→2→3→4,需要携带 19+17=36 个守护精灵; 如果小 E 走路径 1→3→2→4,需要携带 17+15=32 个守护精灵。 综上所述,小 E 最少需要携带 32 个守护精灵。

解释2

小 E 无法从 1 号节点到达 3 号节点,故输出-1。

一道题思考了两天 才比较接近正解 当然最后一些地方的处理还是看了题解才搞定的..毕竟我那么菜啊 习惯了压行可能压行压的有些恶心人了

题意:求一条1~n的路径 并且满足路径上两种权值的最大值最小 最大值最小 很快想到二分 但是想了一会觉得这题实在没有什么二分的地方 就先放弃了 后来想了想 我可以枚举每条边 类似生成树的想法 但是着实没想清楚 因为有两个量在干扰 所以看了题解说 :不妨固定一个量(即排下序 然后另一个量再搞一搞

怎么做 类似最小生成树的办法 先按照a排序 然后按照这个顺序往里面添加边 先判断一下两个点添加之后是否会成环 如果是 那么就比较下原来的和现在的大小 如果原来比现在大那么我现在的有可能比过去的最优答案好 反之continue 因为a是递增.. 为什么这样对的?蒟蒻我想了很久 类似kruskal的方法?既然a已经是升序了 那么这次我新加入的一条边除非b小 才有可能变成a+b最小 你问我a有可能不是这次我要1~n经过的路径..没事我直接把路径切出来即可找到路径上最大的b 再加上我的a因为是递增 即使不在路径上 那么也会因为之前算过被忽略掉 注意断开原来的时候是断开原来的x,y 这题因为边权不好处理 所以再设立一个点 link(x,tmp)link(tmp,y) tmp的值是边权权值 为什么非要构建一棵树 为什么树是对的 首先只有一个权值 一棵树好想确实是对的 两个权值的话 因为我按照其中一个排好序了这时候我维护的这棵树相当于是另一个关键字的mst 相当于是我枚举了其中一个关键字 然后+另一个关键字mst上的值去每次验证 大概这个意思吧qwq蒟蒻我菜死的

#include<cstdio>
#include<algorithm>
#define N 220000
#define M 110000
#define inf 0x3f3f3f3f
using namespace std;
inline char gc(){
    static char now[1<<16],*S,*T;
    if (T==S){T=(S=now)+fread(now,1,1<<16,stdin);if (T==S) return EOF;}
    return *S++;
}
inline int read(){
    int x=0,f=1;char ch=gc();
    while(ch<'0'||ch>'9') {if (ch=='-') f=-1;ch=gc();}
    while(ch<='9'&&ch>='0'){x=x*10+ch-'0';ch=gc();}
    return x*f;
}
struct node{int x,y,a,b;}data[M];
int fat[N],fa[N],c[N][2],id[N],v[N],rev[N],n,m,top,q[N];
inline bool cmp(const node &a,const node &b){return a.a==b.a?a.b<b.b:a.a<b.a;}
inline int find1(int x){while(x!=fat[x]) x=fat[x]=fat[fat[x]];return x;}
inline void update(int x){
    id[x]=x;int l=c[x][0],r=c[x][1];
    if (v[id[l]]>v[id[x]]) id[x]=id[l];
    if (v[id[r]]>v[id[x]]) id[x]=id[r];
}
inline void pushdown(int x){
    int l=c[x][0],r=c[x][1];if (!rev[x]) return;
    rev[l]^=1;rev[r]^=1;rev[x]=0;swap(c[x][0],c[x][1]);
}
inline bool isroot(int x){return c[fa[x]][0]!=x&&c[fa[x]][1]!=x;}
inline void rotate(int x){
    int y=fa[x],z=fa[y];
    if (!isroot(y)) c[z][c[z][1]==y]=x;
    int l=c[y][1]==x,r=l^1;
    fa[c[x][r]]=y;fa[y]=x;fa[x]=z;
    c[y][l]=c[x][r];c[x][r]=y;update(y);update(x);
}
inline void splay(int x){
    q[top=1]=x;for (int i=x;!isroot(i);i=fa[i])q[++top]=fa[i];
    while(top) pushdown(q[top--]);
    while(!isroot(x)){
        int y=fa[x],z=fa[y];
        if (!isroot(y)){if (c[y][0]==x^c[z][0]==y) rotate(x);else rotate(y);}rotate(x);
    }
}
inline void access(int x){for (int t=0;x;t=x,x=fa[x]) splay(x),c[x][1]=t,update(x);}
inline void makeroot(int x){access(x);splay(x);rev[x]^=1;}
inline void link(int x,int y){makeroot(x);fa[x]=y;}
inline void split(int x,int y){makeroot(x);access(y);splay(y);}
inline void cut(int x,int y){split(x,y);fa[x]=c[y][0]=0;update(y);}
int main(){
    freopen("bzoj3669.in","r",stdin);
    n=read();m=read();
    for (int i=1;i<=m;++i){data[i].x=read();data[i].y=read();data[i].a=read();data[i].b=read();}
    sort(data+1,data+m+1,cmp);for (int i=1;i<=n;++i) fat[i]=i;int ans=inf;
    for (int i=1;i<=m;++i){
        int x=data[i].x,y=data[i].y;
        int fx=find1(x),fy=find1(y);
        if (fx!=fy) fat[fx]=fy,v[n+i]=data[i].b,id[n+i]=n+i,link(x,n+i),link(n+i,y);
        else{
            split(x,y);if (v[id[y]]<=data[i].b) continue;int idd=id[y];cut(data[idd-n].x,idd);
            cut(data[idd-n].y,idd);v[n+i]=data[i].b;id[n+i]=n+i;link(x,n+i);link(y,n+i);
        }if (find1(1)==find1(n)) split(1,n),ans=min(ans,data[i].a+v[id[n]]);
    }if (ans==inf)puts("-1");else printf("%d\n",ans);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值