UVa 1623 神龙喝水(Enter The Dragon)

本文介绍了一种优化的湖水管理算法,通过使用预处理数组和集合数据结构,实现了在不下雨的日子里合理分配湖水,避免水灾的发生。该算法利用二分查找和set的特性,确保了整体操作在O(nlogn)的时间复杂度内完成。

题意:
神龙可以在没有下雨的时候喝掉一湖水, 一开始所有河都有水,另外,如果一个湖已经有水了,再下雨,这个湖就会闹水灾,如果一个湖原本没水,下雨了,就满水了,此时不会闹水灾,问是否可以做到不闹水灾,如果可以输出“YES”,且输出不下雨天喝哪河的水,也可以不喝水,这个时候输出0,否则输出“NO”

分析:
紫书上说需要优化算法 看到1e6的数据,很自然的想到nlogn的算法
怎么样才可以nlogn呢?
最好是保存每个节点之前下雨的那个相同点 例如 0 0 1 0 1
比如1湖在第三天和第五天都下雨了
那么 pre[1] = 3
由于一开始水都是满的 因此 pre[1] = 0 表示第0天每个湖都是满水的(下雨了)
每一次把对应湖的上次pre保存
这样需要On的时间
当第三天的时候这个时候置 pre[1] = 3
表明对接下来一个1湖下雨来说, 在第三天的时候下过了,也就是要找到从第三天到下一个一湖下雨天是否存在一个位置
好在我们在没下雨的时候把那个没下雨的天insert进去了,具体表现就是第四天的时候我们insert了一个4
这样在第五天的时候,发现从第三天二分查找一个位置有一个4可以。
那这样就行,顺便把4这个节点移除了
由于二分查找和set删除节点均为logn的时间,因此总时间可以在nlogn的时间内完成。

代码:

#include<bits/stdc++.h>
#define LL long long
#define ms(s) memset(s, 0, sizeof(s))
#define REP(i, a, b) for(int i = (a); i < (b); i++)
#define INF 0X7fffffff
using namespace std;
const int maxn = 1e6 + 10;
int rain[maxn];
int pre[maxn];
int ans[maxn];

int main() {
    // freopen("in.txt", "r", stdin);
    // freopen("out.txt", "w", stdout);
    ios::sync_with_stdio(false);
    cin.tie(0);
    int T;
    cin >> T;
    while(T--) {
        set<int> s;
        int n, m;
        cin >> n >> m;
        ms(pre), ms(ans);
        bool flag = true;
        REP(i, 0, m) cin >> rain[i];
        REP(i, 0, m) {
            if(rain[i] == 0) {
                s.insert(i);
                continue;
            }
            ans[i] = -1; //表示这一天不需要喝水
            auto it = s.lower_bound(pre[rain[i]]);
            if(it == s.end()) {
                flag = false;
                break;
            }
            pre[rain[i]] = i;
            ans[*it] = rain[i];
            s.erase(it);
        }
        if(!flag) {
            cout << "NO" << endl;
            continue;
        }
        bool first = true;
        cout << "YES" << endl;
        REP(i, 0, m) {
            if(ans[i] == -1) continue;
            if(first) cout << ans[i];
            else cout << " " << ans[i];
            first = false;
        }
        cout << endl;
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值