关闭

车站分级 (线段树优化建边 拓扑序最长路)

标签: 线段树
32人阅读 评论(0) 收藏 举报
分类:

车站分级(加强版)

10.11

思路:
基本方法就是等级高的车站向等级低的车站连边,最后跑拓扑序的最长路就是ans。
线段树优化建边的拓扑排序(线段树的神奇应用)。
先是建虚点优化,边数优化为2*n,但是发现建边的复杂度是nm,考虑线段树优化。
注意到经停站把车站序列划分成了多个区间,每个区间对应O(log)个线段树上的节点,因此连边时可以把边数由O(nm)优化到O(m*log(n))。线段树中的上下层节点先连好边,区间整体连边的时候直接把线段树上零散的块连在虚点上即可。具体细节可以参考代码。

#include <cstdio>
#include <queue>
#include <algorithm>
#define N 400010
using namespace std;

struct Edge{
    int to, nxt;
}ed[N * 20];

queue <int> q;

struct node{
    int id;
    node *ls, *rs;
}pool[N * 20], *tail = pool, *root;

int n, m, idc=0, tot=0, ans=0;
int head[N], a[N], mrk[N], re[N], dis[N], in[N];

void adde(int u, int v){
    in[v]++;
    ed[++idc].to = v;
    ed[idc].nxt = head[u];
    head[u] = idc;
}

node *build(int lf, int rg){
    node* nd = ++tail;
    nd->id = ++tot;
    if(lf == rg){
        re[lf] = nd->id;
        mrk[nd->id] = 1;//只有最底层的是实点,计入总长 
        nd->ls = nd->rs = 0;
        return nd;
    }
    int mid = (lf + rg) >> 1;
    nd->ls = build(lf, mid);
    nd->rs = build(mid+1, rg);
    adde(nd->ls->id, nd->id);//虚点 
    adde(nd->rs->id, nd->id);
    return nd;
}

void modify(node *nd, int lf, int rg, int L, int R, int pos){
    if(L<=lf && rg<=R){
        adde(nd->id, pos);
        return ;
    }
    int mid = (lf + rg) >> 1;
    if(L <= mid) modify(nd->ls, lf, mid, L, R, pos);
    if(R > mid) modify(nd->rs, mid+1, rg, L, R, pos);
}

void solve(){//按照拓扑序跑一个最长路 
    for(int i=1; i<=tot+m; i++)
        if( !in[i] ){
            q.push(i);
            if( mrk[i] ) dis[i] = 1;
            else dis[i] = 0;
        }
    while( !q.empty() ){
        int u = q.front(); q.pop();
        for(int i=head[u]; i; i=ed[i].nxt){
            int v = ed[i].to;
            in[v]--;
            if( !in[v] ){
                if( mrk[v] ) dis[v] = max(dis[v], dis[u] + 1);
                else dis[v] = max(dis[v], dis[u]);
                q.push(v);
            }
        }
    }
}

int main(){
    //freopen("c.in", "r", stdin);
    //freopen("c.out", "w", stdout);
    scanf("%d%d", &n, &m);
    root = build(1, n);
    for(int i=1; i<=m; i++){
        int t; scanf("%d",&t);
        for(int k=1; k<=t; k++){
            scanf("%d", &a[k]);//线段树中一共有tot个点 
            adde(tot+i, re[a[k]]);//tot+i表示第i条路径的虚点 re是在线段树中的pos 
        }
        for(int k=2; k<=t; k++){
            int R = a[k] - 1, L = a[k-1] + 1;
            if(L <= R) modify(root, 1, n, L, R, tot+i);//这个区间中所有的车站向虚点连边 
        }
    }
    solve();
    for(int i=1; i<=n; i++)
        ans = max(ans, dis[i]);
    printf("%d\n", ans);
    return 0;
}
0
0

查看评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
    个人资料
    • 访问:18351次
    • 积分:2757
    • 等级:
    • 排名:第12988名
    • 原创:270篇
    • 转载:1篇
    • 译文:0篇
    • 评论:2条
    文章分类
    最新评论