Timus Online Judge 2055 Urban Geography 动态树维护mst(第44case超时了)---留着以后做吧

2055. Urban Geography

Time limit: 2.0 second
Memory limit: 128 MB
Android Vasya prepares a project in Urban geography. The aim of the project is to improve the infrasructure of the city he lives in.
Now the city consists of  n districts, some of which are connected by roads. Using these roads one can get from any district to any other district of the city by car. Vasya thinks that such big amount of roads makes citizens use their own cars instead of walking or cycling. He wants to close as many roads for cars as possible and turn them into boulevards. Of course, Vasya wants to keep the possibility to get from any district to any other district of the city by car using roads.
Now citizens pay for using roads, and prices for different roads may vary very much. Vasya thinks that leaving open some expensive and some cheap roads at the same time is not a good idea beacuse it can increase social tension in the city. That’s why he wants to minimize the price spread between the most expensive and the cheapest roads. Help Vasya choose the roads to keep open.

Input

The first line contains integers  n и  m that are the number of city districts and roads correspondingly (2 ≤  n ≤ 50 000;  n − 1 ≤  m ≤ 50 000). The next  m lines contain triples of integers  a ib i and  c i, meaning that between the city districts  a i and  b i there is a road with the price  c i (1 ≤  a ib i ≤  na i ≠  b i; 1 ≤  c i ≤ 10 9). There can be several roads between two districts.

Output

In the only line output the sequence of integers — numbers of the roads which should be kept open in the city. The roads are numbered as they appear in the input data. If there are several solutions, output any of them.

Samples

input output
3 3
1 2 1
2 3 3
3 1 4
2 3
4 5
1 2 1
2 3 1
1 3 2
1 4 2
2 4 1
1 2 5
Problem Author: Nikita Burlakov
Problem Source: Ural Sport Programming Championship 2015


构建一棵树,是的最长边-最短边最小。

用动态树维护mst。更新最小距离。

动态树的建发:

对每个点建立一个结点。对每条边建立一个结点。加边<u,v>的时候,如果u,v在同一棵树上

把u,v,access到同一个splay中,找到边最小的结点x,cut(x,v),cut(x,u)

把u,v分别变成自己所在树的根,然后对于边建立点x,link(x,u),link(x,v)即可。

动态树要维护的是树中边的权值。

44case T了一天了。实在没办法,换一种方法做吧,下次回来做这题



#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
#include<set>
using namespace std;
#define maxn 400007
#define inf  1000000000
int cnt = 0;
struct Node{
    Node *fa,*ch[2];
    bool root,rev;
    int val,mval;
    Node(){}
    Node* init(int v,Node *f){
        fa = f;
        ch[0] = ch[1] = f;
        root = true;
        val = mval = v;
        rev = false;
        return this;
    }
    void update(){
        mval = val;
        if(ch[0]->mval < mval)
            mval = ch[0]->mval;
        if(ch[1]->mval < mval)
            mval = ch[1]->mval;
    }
    void update_rev(){
        if(val == inf + 10000000)return ;
        rev = !rev;
        swap(ch[0],ch[1]);
    }
    void pushdown(){
        if(rev){
            ch[0]->update_rev();
            ch[1]->update_rev();
            rev = false;
        }
    }
    void rotate(){
        Node *f = fa, *ff = f->fa;
        int t = (f->ch[1] == this);
        if(f->root)
            root = true, f->root = false;
        else ff->ch[ff->ch[1] == f] = this;
        fa = ff;
        f->ch[t] = ch[t^1];
        ch[t^1]->fa = f;
        ch[t^1] = f;
        f->fa = this;
        f->update();
    }
    void push(){
        if(!root) fa->push();
        pushdown();
    }
    void splay(){
        push();
        Node *f, *ff;
        while(!root){
            f = fa,ff = f->fa;
            if(!f->root)
                if((ff->ch[1]==f)&&(f->ch[1] == this)) f->rotate();
                else rotate();
            rotate();
        }
        update();
    }
};
Node pool[maxn];
Node *nil,*tree[maxn];

void init(){
    cnt = 1;
    nil = tree[0] = pool;
    nil->val = nil -> mval = inf+10000000;
}

//将x到树根的路径并成一条path******
Node *access(Node *x){
    Node *y = nil;
    while(x != nil){
        x->splay();
        x->ch[1]->root = true;
        (x->ch[1] = y)->root = false;
        x->update();
        y = x;
        x = x->fa;
    }
    return y;
}
//将结点x变成树根******
void be_root(Node *x){
    //cout<<x->val-inf<<endl;
    access(x);
    x->splay();
    x->update_rev();
    //update_rev(x);
}
//将x连接到结点f上******
void link(Node *x, Node *f){
    be_root(x);
    x->fa = f;
}
//将x,y分离******
void cut(Node *x,Node *y){
    be_root(x);
    access(x);
    y->splay();
    y->fa = nil;
}
Node *findminNode(Node *x){
    if(x->val <= x->ch[0]->mval && x->val <= x->ch[1]->mval)
        return x;
    if(x->ch[0]->mval < x->ch[1]->mval)
        return findminNode(x->ch[0]);
    return findminNode(x->ch[1]);
}
Node *findRoot(Node *x){
    if(x->ch[0] == nil) return x;
    return findRoot(x->ch[0]);
}
struct Edge{
    int u,v,w;
    Edge (){}
    int getn(){
        int n = 0;
        char x;
        while(1){
            x = getchar();
            if(x == ' ' || x == '\n') return n;
            n = n*10+x-'0';
        }
    }
    void init(){
        u = getn();
        v = getn();
        w = getn();
    }
};
Edge edge[maxn];
int fa[100007];
int find(int u){
    if(fa[u]  == u) return u;
    return fa[u] = find(fa[u]);
}

multiset<int>haha;
int comp1(int a,int b){
    return edge[a].w < edge[b].w;
}

int id[100007];
int main(){
    //freopen("j.in","r",stdin);
    //freopen("jl.out","w",stdout);
    int n,m;
    while(scanf("%d%d",&n,&m)!=EOF){
        haha.clear();
        //edge.clear();
        Edge e;
        getchar();
        for(int i = 0;i < m; i++){
            edge[i].init();
            if(edge[i].u == edge[i].v){
                m--;
                i--;
                continue;
            }
            id[i] = i;
        }
        sort(id,id+m,comp1);
        init();
        for(int i = 1;i <= n;i++)
            tree[i] = pool[cnt++].init(inf+i,nil);
        int num = n,ans=1000000000,be,en;
        Node *x,*y,*z;
        for(int i = 0;i < m; i++){
            int u = edge[id[i]].u,v = edge[id[i]].v,w=edge[id[i]].w;
            haha.insert(w);
            z = pool[cnt++].init(w,nil);
            be_root(tree[u]);
            x = access(tree[v]);
            y = findRoot(x);
            if(tree[u] == y){
                x = findminNode(x);
                cut(x,tree[u]);
                cut(x,tree[v]);
                haha.erase(haha.find(x->val));
                num++;
            }
            link(tree[u],z);
            link(tree[v],z);
            num--;
            if(num == 1){
                int mval = *haha.begin();
                if(w - mval < ans){
                    ans = w - mval;
                    be = mval;
                    en = w;
                }
            }
        }
        num = n;
        for(int i = 0;i <= n; i++)
            fa[i] = i;
        for(int i = 0;i < m; i++){
            e = edge[i];
            if(e.w >= be && e.w <= en){
                int f1 = find(e.u);
                int f2 = find(e.v);
                if(f1 != f2){
                    fa[f1] = fa[f2];
                    if(num != n) putchar(' ');
                    printf("%d",i+1);
                    num--;
                }
            }
        }
        printf("\n");
    }
    return 0;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

GDRetop

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值