Gym 102059D Dumae(拓扑排序+优先队列贪心)

原题地址:https://codeforces.com/gym/102059/problem/D

题意: n n n 个人要排成一排,每个人想要站的位置都是一个区间,同时还有一些形如“ x x x必须站在 y y y左边”的限制,构造一组方案。

思路:如果只有后面的 x x x y y y的前面的限制,那么这就是一个简单的拓扑排序。但是这里还有区间的限制。
考入如果有 x x x点的范围是 [ l 1 , r 1 ] [l_1,r_1] [l1,r1], y y y点的范围是 [ l 2 , r 2 ] [l_2,r_2] [l2,r2],并且 x x x点在 y y y点的前面。
那么显然,x点的范围一定是 [ l 1 , m i n ( r 1 , r 2 − 1 ) ] [l_1,min(r_1,r_2-1)] [l1,min(r1,r21)],经过这种处理之后,所有点的所在范围都是在一个区间内且一条链上的都是不相交的。
所以我们拓扑排序构造,把所有入度为 0 0 0的点都加入到一个优先队列中,注意是个最小堆。
然后我们把所有起点小于当前要处理的位置 p o s pos pos的点都加入到另外一个优先队列中,这是个最小堆。表示把所有符合条件的让区间的右端点 r r r小的先填入。
就这样子贪心的构造。

#include <bits/stdc++.h>

#define eps 1e-8
#define INF 0x3f3f3f3f
#define PI acos(-1)
#define lson l,mid,rt<<1
#define rson mid+1,r,rt<<1|1
#define CLR(x, y) memset((x),y,sizeof(x))
#define fuck(x) cerr << #x << "=" << x << endl;
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const int seed = 131;
const int maxn = 3e5 + 5;
const int mod = 1e9 + 7;
int n, m;
int l[maxn], r[maxn];
vector<int> G[maxn];
int into[maxn];
int vis[maxn];

int find(int u) {
    if (vis[u]) return r[u];
    vis[u] = 1;
    for (int i = 0; i < (int) G[u].size(); i++) {
        int v = G[u][i];
        r[u] = min(r[u], find(v) - 1);
    }
    return r[u];
}

//存放拓扑中的点,入度为0
struct node {
    int l, r, id;

    //l小的在前面
    friend bool operator<(node a, node b) {
        return a.l > b.l;
    }
};

typedef pair<int, int> pii;
int ans[maxn];

int main() {
#ifndef ONLINE_JUDGE
    freopen("in.txt", "r", stdin);
#endif
    scanf("%d%d", &n, &m);
    for (int i = 1; i <= n; i++) {
        scanf("%d%d", &l[i], &r[i]);
    }
    for (int i = 1; i <= m; i++) {
        int u, v;
        scanf("%d%d", &u, &v);
        G[u].push_back(v);
        into[v]++;
    }
    priority_queue<node> q;
    priority_queue<pii> pq;
    for (int i = 1; i <= n; i++) {
        r[i] = find(i);
        if (into[i] == 0) {
            q.push(node{l[i], r[i], i});
        }
    }

    int now = 1;

    while (1) {
        while (!q.empty() && q.top().l <= now) {
            node tmp = q.top();
            q.pop();
            pq.push(make_pair(-tmp.r, tmp.id));
        }
        if (pq.empty()) break;

        pii tmp = pq.top();
        pq.pop();
        int u = tmp.second;
        if (r[u] < now) {
            printf("-1\n");
            return 0;
        }
        ans[now++] = u;
        for (int i = 0; i < (int) G[u].size(); i++) {
            int v = G[u][i];
            into[v]--;
            if (into[v] == 0) q.push(node{l[v], r[v], v});
        }
    }

    if (now <= n) printf("-1\n");
    else {
        for (int i = 1; i <= n; i++) {
            printf("%d\n", ans[i]);
        }
    }
    return 0;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值