【BZOJ3669】【codevs3314】魔法森林,写作LCT,读作SPFA

传送门1
传送门2
写在前面:我要种树!!
思路:
SPFA:理论复杂度很蛋疼,但实际效果不错,按a排序(b也可以)后从小到大枚举每条边,dis数组不必清空,每次往队列里扔进当前边的两个端点进行拓展,ans=min(ans,dis[n]+v[i]),v[i]指当前边i的a,dis[n]记录的是1-n路径上最大的b,每次扩展走的路径必须是之前已经枚举过的边,这样才能保证1-n路径上的a的最大值不大于当前边i的a
注意自环
代码:

#include<bits/stdc++.h>
using namespace std;
int tot=1,n,m,ans=0x7fffffff;
int first[50010],dis[50010];
struct edge
{
    int u,v,A,B,next;
}e[200010];
struct os
{
    int id,x;
    bool operator <(const os other)const
    {
        return x<other.x;
    }
}qu[100010];
bool flag[200010],vis[50010]; //flag记录之前枚举过的边,vis判断点是否在队列中
queue<int> q;
void add(int x,int y,int a,int b)
{
    e[++tot].u=x;
    e[tot].v=y;
    e[tot].A=a;
    e[tot].B=b;
    e[tot].next=first[x];
    first[x]=tot;
}
main()
{
    int a,b,c,d,k;
    scanf("%d%d",&n,&m);
    for (int i=1;i<=m;i++)
        scanf("%d%d%d%d",&a,&b,&c,&d),
        add(a,b,c,d),
        qu[i].id=tot,
        qu[i].x=c,
        add(b,a,c,d);
    sort(qu+1,qu+m+1);
    memset(dis,63,sizeof(dis));
    dis[1]=0;
    if (n==1) printf("0"),exit(0); 
    for (int i=1;i<=m;i++)
    {
        int now=qu[i].id;
        flag[now]=flag[now^1]=1;
        if (!vis[e[now].v]) q.push(e[now].v),vis[e[now].v]=1;
        if (!vis[e[now].u]) q.push(e[now].u),vis[e[now].u]=1;
        while (!q.empty())
        {
            k=q.front();
            q.pop();
            for (int j=first[k];j;j=e[j].next)
            if (flag[j]&&dis[e[j].v]>max(dis[k],e[j].B))
            {
                dis[e[j].v]=max(dis[k],e[j].B);
                if (!vis[e[j].v]) vis[e[j].v]=1,q.push(e[j].v);
            }
            vis[k]=0;
        }
        if (dis[n]>1000000) continue;
        ans=min(ans,dis[n]+qu[i].x);
    }
    if (ans>1000000) printf("-1");
    else printf("%d",ans);
}

LCT:并查集维护点之间的连通性,在LCT中把每条边缩成带有权值的节点,而每个点没有权值,与它的边在LCT中对应的节点相连。同样的按a升序排一遍,每次枚举边i(端点为u,v),如果u,v不连通就link一下,联通就的话就找出u-v路径上的最大权值点j(就是b权值最大的边),如果val[j]<=val[i],那么在有j时走i一定不会更优;若val[j]>val[i]决策不定,但我们也可以cut掉j,把ilink上,因为我们每次枚举边时都会求下1-n的答案,留下i可能会更新答案,而且由于形成一个环,j的存在也不会影响答案,但它影响了LCT的结构,所以把它删去
注意:BZOJ上要把数组开大一点,不然codevs上A的程序在那上面可能会RE
代码:

#include<bits/stdc++.h>
using namespace std;
int n,m,ans=0x7fffffff;
int stacks[400010],father[400010];
struct edge
{
    int u,v,A,B;
    bool operator <(const edge other)const
    {
        return A<other.A;
    }
}e[400010];
struct tree
{
    int fa,ch[2],lazy,data,maxn;
}a[400010];
int find(int x)
{
    if (father[x]!=x) father[x]=find(father[x]);
    return father[x];
}
void ct(int x)
{
    int l=a[x].ch[0],r=a[x].ch[1];
    a[x].maxn=(a[a[l].maxn].data>a[a[r].maxn].data?a[l].maxn:a[r].maxn);
    a[x].maxn=(a[a[x].maxn].data>a[x].data?a[x].maxn:x);
}
void pushdown(int x)
{
    if (!a[x].lazy) return;
    a[a[x].ch[0]].lazy^=1;
    a[a[x].ch[1]].lazy^=1;
    swap(a[x].ch[0],a[x].ch[1]);
    a[x].lazy=0;
}
bool isroot(int x)
{
    return a[a[x].fa].ch[0]!=x&&a[a[x].fa].ch[1]!=x;
}
void rorate(int x,bool mk)
{
    int y=a[x].fa;
    a[x].fa=a[y].fa;
    if (!isroot(y))
    {
        if (a[a[y].fa].ch[0]==y) a[a[y].fa].ch[0]=x;
        else a[a[y].fa].ch[1]=x;
    }
    a[y].ch[!mk]=a[x].ch[mk];
    a[a[x].ch[mk]].fa=y;
    a[y].fa=x;
    a[x].ch[mk]=y;
    ct(y);
    ct(x);
}
void splay(int x)
{
    int y,cnt=0,k=x;
    stacks[++cnt]=k;
    while (!isroot(k)) k=a[k].fa,stacks[++cnt]=k;
    for (int i=cnt;i;i--) pushdown(stacks[i]);
    while (!isroot(x))
    {
        y=a[x].fa;
        if (isroot(y))
        {
            int t=a[y].fa; 
            if (a[y].ch[0]==x) rorate(x,1);
            else rorate(x,0);
            a[x].fa=t;
        }
        else if (a[a[y].fa].ch[0]==y)
        {
            if (a[y].ch[0]==x) rorate(y,1);
            else rorate(x,0);
            rorate(x,1);
        }
        else
        {
            if (a[y].ch[1]==x) rorate(y,0);
            else rorate(x,1);
            rorate(x,0);
        }
    }
}
void access(int x)
{
    for (int y=0;x;y=x,x=a[x].fa)
        splay(x),
        a[x].ch[1]=y,
        ct(x);
}
void link(int x,int y)
{
    access(x);
    splay(x);
    a[x].lazy^=1;
    a[x].fa=y;
    splay(x);
}
void cut(int x,int y)
{
    access(x);
    splay(x);
    a[x].lazy^=1;
    access(y);
    splay(y);
    a[x].fa=a[y].ch[0]=0;
    ct(y); 
}
int ask(int x,int y)
{
    access(x);
    splay(x);
    a[x].lazy^=1;
    access(y);
    splay(y);
    return a[y].maxn;
}
main()
{
    scanf("%d%d",&n,&m);
    for (int i=1;i<=n;i++) father[i]=i;
    for (int i=1;i<=m;i++)
    scanf("%d%d%d%d",&e[i].u,&e[i].v,&e[i].A,&e[i].B);
    sort(e+1,e+m+1);
    for (int i=1;i<=m;i++)
    {
        int p=find(e[i].u),q=find(e[i].v);
        a[n+i].data=e[i].B;
        a[n+i].maxn=n+i;
        if (p!=q) father[p]=q,link(e[i].u,n+i),
            link(e[i].v,n+i);
        else
        {
            int t=ask(e[i].u,e[i].v);
            if (e[t-n].B>e[i].B)
            {
                cut(e[t-n].u,t);
                cut(e[t-n].v,t);
                link(e[i].u,i+n);
                link(e[i].v,i+n);
            }
        }
        if (find(1)==find(n)) ans=min(ans,e[i].A+a[ask(1,n)].data);
    }
    if (ans>1000000) printf("-1");
    else printf("%d",ans);
}

……
……
……
为什么两个程序一加快速读入就会出问题啊(┬_┬)

你可能感兴趣的:(【BZOJ3669】【codevs3314】魔法森林,写作LCT,读作SPFA)