cf605D. Board Game(BFS 树状数组 set)

题意

题目链接

\(n\)张牌,每张牌有四个属性\((a, b, c, d)\),主人公有两个属性\((x, y)\)(初始时为(0, 0))

一张牌能够被使用当且仅当\(a < x, b < y\),使用后\(x\)会变为\(c\)\(y\)会变为\(d\)

问使用第\(n\)张牌的最小步数

Sol

直接从\((0, 0)\)开始大力BFS,那么第一次到达时就是最小的,同时记录一下前驱

现在的问题就是如何知道哪些点可以选,也就是找到所有\(a < x, b < y\)的点,可以直接树状数组+set维护

由于保证了每个元素只出现一次,因此总复杂度为\(O(nlog^2n)\)

#include<bits/stdc++.h> 
#define Pair pair<int, int>
#define MP(x, y) make_pair(x, y)
#define fi first
#define se second
#define pb(x) push_back(x)
// #define int long long 
#define LL long long 
#define pt(x) printf("%d ", x);
#define Fin(x) {freopen(#x".in","r",stdin);}
#define Fout(x) {freopen(#x".out","w",stdout);}
using namespace std;
const int MAXN = 2e5 + 10, INF = 1e9 + 10, mod = 1e9 + 7;
const double eps = 1e-9;
void chmax(int &a, int b) {a = (a > b ? a : b);}
void chmin(int &a, int b) {a = (a < b ? a : b);}
int sqr(int x) {return x * x;}
int add(int x, int y) {if(x + y < 0) return x + y + mod; return x + y >= mod ? x + y - mod : x + y;}
void add2(int &x, int y) {if(x + y < 0) x = x + y + mod; else x = (x + y >= mod ? x + y - mod : x + y);}
int mul(int x, int y) {return 1ll * x * y % mod;}
inline int read() {
    char c = getchar(); int x = 0, f = 1;
    while(c < '0' || c > '9') {if(c == '-') f = -1; c = getchar();}
    while(c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar();
    return x * f;
}
int N, a[MAXN], b[MAXN], c[MAXN], d[MAXN], vis[MAXN], dis[MAXN], pre[MAXN], da[MAXN], num;
#define lb(x) (x & (-x)) 
#define sit set<Pair>::iterator 
set<Pair> T[MAXN];
void Add(int x, int v, int id) {
    for(; x <= num; x += lb(x)) T[x].insert(MP(v, id));
}
vector<int> Query(int p) {
    int x = a[p], y = b[p];
    vector<int> res;
    for(; x; x -= lb(x)) {
        set<Pair> &now = T[x];
        while(1) {
            sit it = now.lower_bound(MP(y, 0));
            if(it == now.end()) break;
            res.pb(it -> se); now.erase(it);
        }
    }
    return res;
}

void Des() {
    sort(da + 1, da + num + 1); num = unique(da + 1, da + num + 1) - da - 1;
    for(int i = 1; i <= N; i++) {
        a[i] = num - (lower_bound(da + 1, da + num + 1, a[i]) - da) + 1;
        c[i] = num - (lower_bound(da + 1, da + num + 1, c[i]) - da) + 1;
        if(i != N) Add(c[i], d[i], i);
    }
}
void print(int t) {
    printf("%d\n", dis[t]);
    for(int u = t; ~u; u = pre[u]) printf("%d ", u);
}
void BFS() {
    queue<int> q; q.push(N); pre[N] = -1; dis[N] = 1;
    while(!q.empty()) {
        int p = q.front(); q.pop();
        if(a[p] == num && !b[p]) {print(p); return ;}
        vector<int> nxt = Query(p);
        for(int i = 0, t; i < nxt.size(); i++) {
            if(vis[nxt[i]]) continue; vis[nxt[i]] = 1;
            q.push(t = nxt[i]);
            dis[t] = dis[p] + 1, pre[t] = p;
        }
    }
    puts("-1");
}
signed main() {
    N = read(); bool flag = 0;
    for(int i = 1; i <= N; i++) {
        a[i] = read(), b[i] = read(), c[i] = read(), d[i] = read();
        da[++num] = a[i]; 
        da[++num] = c[i];
        flag |= (!a[i] && !b[i]);
    } 
    if(!flag) return puts("-1");
    Des();
    BFS();
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值