题解 P4376 [USACO18OPEN]Milking Order G

Link

把每只奶牛都看成一个点,题目中有规定点的先后顺序,容易想到拓补排序,把这样的先后顺序看作一条边,点 u 在点 v 前即为连一条 uv 的边,并使点 v 的入度 invinv+1

其中还有一些规定是无法满足的,即出现环时没有拓补排序,题目要求满足前 x 个规定并使 x 最大。

这个东西显然是满足单调性的。因为加了前 x 组边若有环,那对于 y(x,m],加了前 y 组边,必定会有前 x 组边中的环。

于是想到可以二分可加入边的组数 x。每次二分重新加边,跑拓补排序,找到 xmax 后再跑一遍记录答案即可。

因为题目要求字典序最小的拓补排序,所以用小根堆维护入队的点,注意在二分 check 的时候不需要用小根堆,否则复杂度会多乘堆的一个 log,变为 O(nlog2n)虽然本题没什么区别

时间复杂度 O(nlogn)

#include <bits/stdc++.h>
using namespace std;
const int N = 1e5 + 10;
int n, m;
int ld[N], rd[N], c[N << 1], awa;
//ld[i]和rd[i]是存第i组边在所有点中对应的区间
struct edge {
    int to, nxt;
} e[N << 1];
int head[N], cnt;
void add(int u, int v) {
    e[++cnt] = (edge){v, head[u]};
    head[u] = cnt;
}
int in[N];
queue<int> q;
priority_queue<int, vector<int>, greater<int> > p;
int ans[N], tot;
bool check(int x){
    memset(head, 0, sizeof(head));
    memset(in, 0, sizeof(in));
    cnt = tot = 0;
    for(int i = 1; i <= x; i++)
        for(int j = ld[i] + 1; j <= rd[i]; j++)
            add(c[j - 1], c[j]), in[c[j]]++;
    for(int i = 1; i <= n; i++) if(!in[i]) q.push(i);
    while(!q.empty()) {
        int u = q.front();
        q.pop();
        ++tot;
        for(int i = head[u]; i; i = e[i].nxt) {
            int v = e[i].to;
            if(!(--in[v])) q.push(v); 
        }
    }
    return (tot == n);
}
void topu(int x){
    memset(head, 0, sizeof(head));
    memset(in, 0, sizeof(in));
    cnt = tot = 0;
    for(int i = 1; i <= x; i++)
        for(int j = ld[i] + 1; j <= rd[i]; j++)
            add(c[j - 1], c[j]), in[c[j]]++;
    for(int i = 1; i <= n; i++) if(!in[i]) p.push(i);
    while(!p.empty()) {
        int u = p.top();
        p.pop();
        ans[++tot] = u;
        for(int i = head[u]; i; i = e[i].nxt) {
            int v = e[i].to;
            --in[v];
            if(!in[v]) p.push(v); 
        }
    }
}
int main() {
    scanf("%d%d", &n, &m);
    for(int i = 1; i <= m; i++) {
        int t; scanf("%d", &t);
        if(!t) continue;
        ld[i] = awa + 1;
        for(int j = 1; j <= t; j++)
            scanf("%d", &c[++awa]);
        rd[i] = awa;
    }
    int l = 1, r = m, x, mid;
    while(l <= r) {
        mid = (l + r) >> 1;
        if(check(mid)) x = mid, l = mid + 1;
        else r = mid - 1;
    }
    topu(x);
    for(int i = 1; i <= n; i++) printf("%d ", ans[i]);
    return 0;
}
posted @   Terac  阅读(20)  评论(0编辑  收藏  举报  
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
· 25岁的心里话
点击右上角即可分享
微信分享提示