图论训练 车站分级 [数据结构优化建边][拓扑排序]

NOIP普及组原题疯狂加难度的hard版

车站分级(c.cpp,0.5s, 256MB)

【描述】
一条单向的铁路线上,依次有编号为 1, 2, …, n 的 n 个火车站。每个火车站都有一个级别,最低为 1 级。现有若干趟车次在这条线路上行驶,每一趟都满足如下要求:如果这趟车次停靠了火车站 x,则始发站、终点站之间所有级别大于等于火车站 x 的都必须停靠。(注意:起始站和终点站自然也算作事先已知需要停靠的站点)现有 m 趟车次的运行情况(全部满足要求),试推算这 n 个火车站至少分为几个不同的级别。
【输入】
第一行包含 2 个正整数 n, m。
接下来 m 行,首先是一个正整数 si(2 ≤ si ≤ n),表示第 i 趟车次有 si 个停靠站;接下来有 si个正整数,表示所有停靠站的编号,从小到大排列。每两个数之间用一个空格隔开。输入保证所有的车次都满足要求。
【输出】
一行一个整数,表示车站最少划分的级别数。
【样例输入】
9 3
4 1 3 5 6
3 3 5 6
3 1 5 9
【样例输出】
3
【限制与约定】
对于10%的数据,n,m<=100。
对于40%的数据,n,m<=5000。
对于100%的数据,n,m<=100000,所有si的和不超过100000。

思考

这玩意儿看着就很疯狂,用到了建立虚点的优化,然后发现建边超时了,为了防止这种情况发生,然后就以数据结构(or 倍增建边……)优化,一边顶多条边。
同时线段树的上下层节点也互相连边,关联虚点。

#define PN "c"
#include <cstdio>
#include <cstring>
#include <algorithm>

const int SIZ = 600000 + 1000;
const int M = 6000000 + 100000;
struct EDGE {int v,upre;}g[M];
int head[SIZ], ne = 0;
int in[SIZ], level[SIZ], pos[SIZ], idc;
bool mark[SIZ];
inline void adde(int u,int v) {in[v]++;g[++ne]=(EDGE){v,head[u]};head[u]=ne;}

#include <queue>
std::queue<int> q;
int n;
void solve() {
    int u, i, v, ans = 1;q.push(0);
    for( i = 1; i <= n; i++ ) if(!in[pos[i]]) q.push(pos[i]), level[pos[i]]=1;
    while(!q.empty()) {
        u = q.front();q.pop();
        for( i = head[u], v; i; i = g[i].upre ) {
            v = g[i].v;
            in[v]--;if(!in[v]) q.push(v);
            level[v]=std::max(level[v],level[u]+mark[v]);
            ans=std::max(ans,level[v]);
        }
    }
    printf("%d\n",ans);
}

struct NODE {
    int id;
    NODE *ls, *rs;
} pool[SIZ], *tail=pool, *root;
NODE *build(int lf,int rg) {
    NODE *nd=tail++;nd->id=++idc;
    if(lf==rg) mark[nd->id]=1, pos[lf]=nd->id;
    else {
        int mid=(lf+rg)>>1;
        nd->ls=build(lf,mid);
        adde(nd->ls->id,nd->id);
        nd->rs=build(mid+1,rg);
        adde(nd->rs->id,nd->id);
    }
    return nd;
}
void addedge(NODE *nd,int lf,int rg,int L,int R,int linkpoint) {
    if(L<=lf&&rg<=R) {
        adde(nd->id,linkpoint); 
        return ;
    }
    int mid=(lf+rg)>>1;
    if(L<=mid) addedge(nd->ls,lf,mid,L,R,linkpoint);
    if(mid<R) addedge(nd->rs,mid+1,rg,L,R,linkpoint);
}

int main() {
    freopen(PN".in","r",stdin);
    freopen(PN".out","w",stdout);
    int m;
    scanf("%d%d",&n,&m);
    root=build(1,n);
    for( int i = 1, j, s, last, now, linkpoint; i <= m; i++ ) {
        scanf("%d%d",&s,&last);linkpoint=++idc;adde(0,linkpoint);adde(linkpoint,pos[last]);
        for( j = 1; j < s; j++ ) {
            scanf("%d",&now);adde(linkpoint,pos[now]);
            addedge(root,1,n,last+1,now-1,linkpoint);
            last = now;
        }
    }
    solve();
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值