codeforces 787d[补]

感觉这套题强行撸下来真是超神了,头发都不知道掉了多少。。
一道线段树+最短路的混合使用,强行给CF教育了一发


题目:http://codeforces.com/problemset/problem/787/D
题目大意:
简单点来说就是构造一个图,构造的方法有
1、u v
2、u [l,r]
3、[l,r] u
这三种情况,每种情况都会可以构成权值为c的边
然后问随意一个点到所有点的最短路是多少

由于点数为1e5个,总的构图次数为1e5次
有可能构成的边为1e10,首先点数为1e5,构成之后所用dijkstra时间复杂度是O( n2 )远远不够
于是想到了djikstra+heap优化的O( nlogn )的算法,但是复杂度还是不够,乘上点数总时间复杂度为O( n2logn )
所以。。还需要继续优化,怎么优化呢
看到区间,应该能yy到线段树这种鬼畜的数据结构吧。。没错就是线段树
我们通过线段树来减少边的条数。。至于怎么减少呢。。

我们可以构建两种线段树,一种是从上指到下的,一种是从下指到上的,他们互相访问的权值为0,
当遇到u [l,r]这种情况的时候,让他去指向线段树中相应的区间,即可减少边数,我们把线段树的每一个区间都看成一个点,这样就能构成一个最短路。。。
然后同样的[l,r] u 这种情况,用线段树的区间去指向点,同样把线段树的区间看成点。
线段树所组成的线段,基本上可以看成是O(2)级别的,平均算作是O(2)吧。。
那基本上,你每次无论是点连区间还是区间连点,所构成的线大概也就2条上下

所以,建图的时候,点数应该*5倍,但是由于线少了,被log了一下,所以时间复杂度应该改为O( nlog2n )
这个时间复杂度,对于这题就够了。。简直鬼畜。。


敢问世间何时对我们这种弱渣连DIV2都要虐成狗,强行教育了一波。
好像对于pair在G++ 5.0版本不能这么使用,代码只能在G++11.0下编译。
dalao们都说。敲个线段树再敲个dijkstra就过了。。。确实。但是脑洞很大Orz

/*
@resouces: codeforces 787D
@date: 2017-3-27
@author: QuanQqqqq
@algorithm: 线段树 + dijkstra nlogn 
*/
#include <bits/stdc++.h>

#define MAXN 500005
#define ll long long

using namespace std;

const ll LLINF = 0x3f3f3f3f3fLL;
typedef pair<ll,int> pli;

struct Edge{
    int to,weight;
    Edge(int _to = 0,int _weight = 0) : to(_to),weight(_weight){}
};

priority_queue<pli > Q;
vector<vector<Edge> > G(MAXN);
bool vis[MAXN];
int id[MAXN][2];
vector<int> vs;
ll d[MAXN];
int idx;

void addEdge(int from,int to,int weight){
    G[from].push_back(Edge(to,weight));
}

void build(int l,int r,int rt,int wh){  //wh0为从上到下的线段树,wh1为从下到上的线段树 
    id[rt][wh] = ++idx;
    if(l == r){
        if(wh == 0){
            addEdge(id[rt][wh],l,0);
        } else {
            addEdge(l,id[rt][wh],0);
        }
        return;
    }
    int mid = l + r >> 1;
    build(l,mid,rt << 1,wh);
    build(mid + 1,r,rt << 1 | 1,wh);
    if(wh == 0){
        addEdge(id[rt][wh],id[rt << 1][wh],0);
        addEdge(id[rt][wh],id[rt << 1 | 1][wh],0);
    } else {
        addEdge(id[rt << 1][wh],id[rt][wh],0);
        addEdge(id[rt << 1 | 1][wh],id[rt][wh],0);
    }
}

void get(int l,int r,int L,int R,int rt,int wh){
    if(L <= l && r <= R){
        vs.push_back(id[rt][wh]);
        return;
    }
    int m = l + r >> 1;
    if(m >= L){
        get(l,m,L,R,rt << 1,wh);
    }
    if(m < R){
        get(m + 1,r,L,R,rt << 1 | 1,wh);
    }
}

void dijkstra(int s,int n){
    for(int i = 1;i <= n;i++){
        vis[i] = false;
        d[i] = LLINF;
    }
    Q.push({-0,s});
    d[s] = 0;
    while(!Q.empty()){
        int u = Q.top().second;
        Q.pop();
        if(vis[u]){
            continue;
        }
        vis[u] = true;
        for(int i = 0;i < G[u].size();i++){
            Edge e = G[u][i];
            int v = e.to,w = e.weight;
            if(w + d[u] < d[v]){
                d[v] = w + d[u];
                Q.push({-d[v],v});
            }
        }
    }
}

void init(int n){
    while(!Q.empty()){
        Q.pop();
    }
    for(int i = 1;i <= n;i++){
        G[i].clear();
    }
}

int main(){
    int n,q,s,l,r,t,u,v,c;
    scanf("%d %d %d",&n,&q,&s);
    init(n * 5);
    idx = n;
    build(1,n,1,0);
    build(1,n,1,1);
    while(q--){
        scanf("%d %d",&t,&u);
        if(t == 1){
            scanf("%d %d",&v,&c);
            addEdge(u,v,c);
        } else if(t == 2) {
            vs.clear();
            scanf("%d %d %d",&l,&r,&c);
            get(1,n,l,r,1,0);
            for(int i = 0;i < vs.size();i++){
                addEdge(u,vs[i],c);
            }
        } else {
            vs.clear();
            scanf("%d %d %d",&l,&r,&c);
            get(1,n,l,r,1,1);
            for(int i = 0;i < vs.size();i++){
                addEdge(vs[i],u,c);
            }
        }
    }
    dijkstra(s,5 * n);
    for(int i = 1;i <= n;i++){
        if(d[i] == LLINF){
            d[i] = -1;
        }
        printf("%lld ",d[i]);
    }
}

另:这套DIV还有dalao直播讲题解。。Orz弱校渣终于得到了dalao的直播真传,厉害了。。
http://www.bilibili.com/video/av9365298/

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值