BZOJ 3669 NOI2014 魔法森林 SPFA

题目大意:

给定一个无向图,每条边有两个权值ai和bi,从1走到N,设路径上a权的最大值为A,b权的最大值为B,求A+B的最小值

首先这题如果只有一个权值就是水题无误……但是多了个权值之后我们就要好好考虑一下了

我们对a排序,枚举a,对于每一次枚举求b权最大值的最小值即可

跑M遍SPFA肯定超时无误 网上很多人写了LInk-Cut-Tree维护动态最小生成树 我的LCT没写明白 就去写了SPFA。。。。

这里要用的SPFA的动态加点(边)法 我们每加一条边 就把边的两端点入队 继续SPFA 不用对f数组进行memset

这方法非常快 比LCT好写了不知多少 然后这里还有剪枝

剪枝1 每加一条边就SPFA自然很浪费 我们发现数据里有a<=30的点 那么很多边的a值会是重复的 我们把a值相同的点统统加入队列 然后SPFA一次解决

剪枝2 对于一条双向边 我们不必向队列中加两个点 我一开始写的是把f值小的加进去 因为小的可以更新大的 后来发现这样还是慢 就直接在主函数中更新f值大的 能更新就加入队列

然后加上堆优化和读入优化 代码就排第一了。。。跑了1400+MS 喵哈哈哈哈哈

#include<ctime>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define M 50500
using namespace std;
struct abcd{
    int to,f,next;
}table[M<<2];
int head[M],tot;
struct edges{
    int x,y,a,b;
}e[M<<1];
bool operator <(edges x,edges y)
{
    return x.a < y.a ;
}
int n,m,ans=0x3f3f3f3f,f[M],heap[M],pos[M],top;
void push_up(int x)
{
    int t=pos[x];
    while(t>1&&f[heap[t]]<f[heap[t>>1]])
        swap(heap[t],heap[t>>1]),swap(pos[heap[t]],pos[heap[t>>1]]),t>>=1;
}
void insert(int x)
{
    heap[++top]=x;
    pos[x]=top;
    push_up(x);
}
void pop()
{
    pos[heap[1]]=0;
    heap[1]=heap[top];
    heap[top--]=0;
    pos[heap[1]]=1;
    int t=2;
    while(t<=top)
    {
        if(t<top&&f[heap[t]]>f[heap[t+1]])
            t++;
        if(f[heap[t]]<f[heap[t>>1]])
            swap(heap[t],heap[t>>1]),swap(pos[heap[t]],pos[heap[t>>1]]),t<<=1;
        else
            break;
    }
}
void SPFA()
{
    int i;
    while(top)
    {
        int x=heap[1];pop();
        for(i=head[x];i;i=table[i].next)
            if(f[table[i].to]>max(f[x],table[i].f))
            {
                f[table[i].to]=max(f[x],table[i].f);
                if(!pos[table[i].to])
                    insert(table[i].to);
                else
                    push_up(table[i].to);
            }
    }
}
void add(int x,int y,int z)
{
    table[++tot].to=y;
    table[tot].f=z;
    table[tot].next=head[x];
    head[x]=tot;
}
char c;
inline void scan(int &x)
{
    x=0;
    do c=getchar(); while(c<'0'||c>'9');
    while(c>='0'&&c<='9')x*=10,x+=c-'0',c=getchar();
}
int main()
{

    //freopen("forest.in","r",stdin);
    //freopen("forest.out","w",stdout);

    int i;
    cin>>n>>m;
    for(i=1;i<=m;i++)
        scan(e[i].x),scan(e[i].y),scan(e[i].a),scan(e[i].b);
    sort(e+1,e+m+1);
    memset(f,0x3f,sizeof f);
    f[1]=0;
    for(i=1;i<=m;i++)
    {
        add(e[i].x,e[i].y,e[i].b);
        add(e[i].y,e[i].x,e[i].b);
        if(f[e[i].x]>f[e[i].y])
        	swap(e[i].x,e[i].y);
        if(max(f[e[i].x],e[i].b)<f[e[i].y])
            f[e[i].y]=max(f[e[i].x],e[i].b),insert(e[i].y);
        if(e[i].a!=e[i+1].a)
            SPFA();
        ans=min(ans,e[i].a+f[n]);
    }
    if(ans==0x3f3f3f3f)
        cout<<-1<<endl;
    else
        cout<<ans<<endl;
    return 0;
}
时间: 2024-10-25 11:58:27

BZOJ 3669 NOI2014 魔法森林 SPFA的相关文章

bzoj 3669: [Noi2014]魔法森林

3669: [Noi2014]魔法森林 Time Limit: 30 Sec  Memory Limit: 512 MB 动点spfa Description 为了得到书法大家的真传,小E同学下定决心去拜访住在魔法森林中的隐士.魔法森林可以被看成一个包含个N节点M条边的无向图,节点标号为1..N,边标号为1..M.初始时小E同学在号节点1,隐士则住在号节点N.小E需要通过这一片魔法森林,才能够拜访到隐士. 魔法森林中居住了一些妖怪.每当有人经过一条边的时候,这条边上的妖怪就会对其发起攻击.幸运的

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

排序搞掉一维, 然后就用LCT维护加边MST. O(NlogN) -------------------------------------------------------------------------------------- #include<cstdio> #include<cctype> #include<cstring> #include<algorithm> using namespace std; const int maxn = 1

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

3669: [Noi2014]魔法森林 Time Limit: 30 Sec  Memory Limit: 512 MBSubmit: 363  Solved: 202[Submit][Status] Description 为了得到书法大家的真传,小E同学下定决心去拜访住在魔法森林中的隐士.魔法森林可以被看成一个包含个N节点M条边的无向图,节点标号为1..N,边标号为1..M.初始时小E同学在号节点1,隐士则住在号节点N.小E需要通过这一片魔法森林,才能够拜访到隐士. 魔法森林中居住了一些妖怪

[BZOJ 3669] [Noi2014] 魔法森林 【LCT】

题目链接:BZOJ - 3669 题目分析 如果确定了带 x 只精灵A,那么我们就是要找一条 1 到 n 的路径,满足只经过 Ai <= x 的边,而且要使经过的边中最大的 Bi 尽量小. 其实就是一个按照 Bi 建立的 MST 上 1 到 n 的路径.只能使用 Ai <= x 的边. 那么,如果我们从小到大枚举 x ,这样可以使用的边就不断增加,就是在加边的同时维护 MST ,用 LCT 来做就可以了. 如果新加入一条边 (u, v, w) ,并且原 MST 上 u 到 v 的路径中边权最大

【BZOJ】3669: [Noi2014]魔法森林(lct)

http://www.lydsy.com/JudgeOnline/problem.php?id=3669 首先看到题目应该可以得到我们要最小化 min{ max{a(u, v)} + max{b(u, v)} } 两个变量不好做...那么我们约束一个a 即按a从小到大排序,依次加边. 发现当有环出现时,去掉的是环中b最大的边. 证明:因为a是从小到大排序,因此此时答案为 a+max{b(u, v)},显然b越小越好. 然后需要link和cut操作... 脑洞开到这里开不动了...........

3669 [Noi2014]魔法森林(LCT,最小生成树)

[题目链接] http://www.lydsy.com/JudgeOnline/problem.php?id=3669 [题意] 给定一个无向图,求1-n的路径中最小的max{ai}+max{bi} [思路] 将边按照a排序.LCT维护关于b的最小生成树. 顺序枚举每条边u,v,如果u,v已经连接则比较u,v路径上的最大边与新边,否则直接相连. 如果1与n连通,则用e.a+max{e.b}更新ans.直观地看,最小生成树上的max{e.b}是1..i条边加入后能够得到的最小b. _max的初值赋

NOI2014 魔法森林

3669: [Noi2014]魔法森林 Time Limit: 30 Sec  Memory Limit: 512 MBSubmit: 106  Solved: 62[Submit][Status] Description 为了得到书法大家的真传,小E同学下定决心去拜访住在魔法森林中的隐士.魔法森林可以被看成一个包含个N节点M条边的无向图,节点标号为1..N,边标号为1..M.初始时小E同学在号节点1,隐士则住在号节点N.小E需要通过这一片魔法森林,才能够拜访到隐士. 魔法森林中居住了一些妖怪.

1685. [NOI2014]魔法森林

1685. [NOI2014]魔法森林 动态SPFA 求两个数的 最优值 我们可以限定一个值 求另一个的最优值 这个题 我们可以 对a排序 每次动态加边 就省去了 每次SPFA的dis和vis数组的清空 cogs上毫无压力 但是在UOJ上有一个额外的测试点 好像专门卡SPFA 毕竟这个正解应该是LCT嘛 1 #include <cctype> 2 #include <cstdio> 3 #include <algorithm> 4 #define max(a,b) a&

【BZOJ3669】[Noi2014]魔法森林 LCT

[BZOJ3669][Noi2014]魔法森林 Description 为了得到书法大家的真传,小E同学下定决心去拜访住在魔法森林中的隐士.魔法森林可以被看成一个包含个N节点M条边的无向图,节点标号为1..N,边标号为1..M.初始时小E同学在号节点1,隐士则住在号节点N.小E需要通过这一片魔法森林,才能够拜访到隐士. 魔法森林中居住了一些妖怪.每当有人经过一条边的时候,这条边上的妖怪就会对其发起攻击.幸运的是,在号节点住着两种守护精灵:A型守护精灵与B型守护精灵.小E可以借助它们的力量,达到自