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;
}