P1983 车站分级 · 拓扑/线段树+虚拟点优化

题解

版本0:差分,差分没办法解决样例1

版本1:拓扑
建图,从级别低的车站流向级别高的车站

版本2:用线段树优化拓扑
就是将区间缩成点了,详情见代码


P1983 车站分级 · 拓扑/线段树+虚拟点优化_第1张图片
P1983 车站分级 · 拓扑/线段树+虚拟点优化_第2张图片


拓扑排序

#include 
using namespace std;
typedef  pair<int,int> pii;
const int N=1e3+10;
bool vis[N];//判断是否停靠
vector<int>g[N];
int n,m,k;
int d[N];//入度
int cnt=0;
int s[N];
bool mp[N][N];//建图 防止重边
int tp(){
    //先搜完级别低的
    int ans=1;
    queue<pii>q;
    for (int i = 1; i <= n; ++i) {
        if(!d[i])q.push({i,1});//车站 级别
    }
    while(q.size()){
        int u=q.front().first;
        int t=q.front().second;
        q.pop();

        for (int i = 0; i <g[u].size(); ++i) {
            int v=g[u][i];
            d[v]--;
            if(!d[v]){
                ans=max(ans,t+1);
                q.push({v,t+1});
            }
        }
    }
    return ans;
}

int main(){
    cin>>n>>m;
    for (int i = 1; i <= m; ++i) {
        cin>>cnt;//停靠的车站的个数
        memset(vis, false, sizeof(vis));
        for (int j = 1; j <= cnt; ++j) {
            cin>>s[j];
            vis[s[j]]= true;
        }
        //建图
        for (int j = s[1]; j <= s[cnt]; ++j) {
            //枚举从起点到终点所有不停靠的车站
            if(vis[j])continue;
            //车站中间的点的级别低于停靠车站 外层级别低的车站
            for (int k = 1; k <= cnt; ++k) {//内层级别高的车站
                if(!mp[j][s[k]]){
                    mp[j][s[k]]=true;
                    g[j].push_back(s[k]);//级别低的点到级别高的点
                    d[s[k]]++;
                }
            }
        }
    }
    cout <<tp() << endl;
    return 0;
}

线段树、虚拟点优化后的

#include 
using namespace std;
const int N=1e6+10;
int n,m,k;

struct edge{
    int next,to;
}e[N];
int head[N],tot;
int d[N];//入度
void add(int u,int v){//级别低u流向级别高v
    d[v]++;
    e[++tot]={head[u],v};
    head[u]=tot;
}

int num[N];//存点i所在的根节点rt
int dis[N];//到达当前节点的消耗的权值 注意这个数组

int tp;//根节点
void build(int l,int r,int rt){
    tp=max(tp,rt);//找到最后一个根节点
    if(l==r){
        dis[rt]=1;
        num[l]=rt;
        return;
    }

    int mid=l+r>>1;
    add(rt<<1,rt);//连接根节点
    build(l,mid,rt<<1);

    add(rt<<1|1,rt);
    build(mid+1,r,rt<<1|1);
}

void update(int l,int r,int rt,int x,int y,int tmp){
    if(x<=l && r<=y){
        add(rt,tmp);//级别低的节点(区间)流向级别高的节点
        return;
    }

    int mid=l+r>>1;
    if(x<=mid) update(l,mid,rt<<1,x,y,tmp);
    if(mid<y) update(mid+1,r,rt<<1|1,x,y,tmp);
}

int ans=1;
int dep[N];//级别 或者说 深度
queue<int>q;
void bfs(){
    for (int i = 1; i <= tp; ++i) {
        if(!d[i]) {
            q.push(i);
            dep[i]=dis[i];//虚拟节点dep[]=dis[]=0
        }
    }

    while(q.size()){
        int u=q.front();
        q.pop();
        for (int i = head[u]; i ; i=e[i].next) {
            int v=e[i].to;
            dep[v]=max(dep[v],dep[u]+dis[v]);//有些节点dis[v]=0 as:tp>n的那些rt节点 所以直接复制上层级别
            ans=max(ans,dep[v]);
            if(!(--d[v]))q.push(v);
        }
    }
}

int cnt;
int st[N];
int main(){
    cin>>n>>m;
    build(1,n,1);//建树
    for (int i = 1; i <= m; ++i) {
        cin>>cnt;
        tp++;//最后一个根节点 设置虚拟节点 dis[tp]=0;
        for (int j = 1; j <= cnt; ++j) {
            cin>>st[j];
        }
        for (int j = 1; j < cnt; ++j) {
            add(tp,num[st[j]]);//直接流向叶子节点
            if(st[j]+1<=st[j+1]-1)//更新中间级别低的区间
                update(1,n,1,st[j]+1,st[j+1]-1,tp);
        }
        add(tp,num[st[cnt]]);
    }

    bfs();
    cout<<ans<<endl;
    return 0;
}

你可能感兴趣的:(洛谷)