ACM-ICPC 2018 南京賽區網絡預賽 Lpl and Energy-saving Lamps

題目鏈接:https://nanti.jisuanke.com/t/30996

題目大意:每個房間有一定的燈,每個月買m個燈泡,每次選擇從左往右第一個燈數比手上的燈泡數少的房間,將這個房間的所有燈泡換掉。然後q次詢問,每次詢問第k個月結束以後已經換好了幾個房間和手上還有幾個燈泡。

我的解法是用一個線段樹維護區間最小值來用logn的複雜度找從左往右第一個燈數比手上的燈泡數少的房間,並更新這個房間的數據,因爲最多詢問到第1e5個月,所以先預處理好每個月結束時的數據即可。

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 100005;
struct
{
    int num,keep;
}z[maxn];
int cnt,num,cntt;
int n,m;
int tmp[maxn];
int minn[maxn * 4];
void build(int l, int r, int rt)
{
    if (l == r)
    {
        minn[rt] = tmp[l];
        return;
    }
    int mid = (l + r) >> 1;
    build(l, mid, rt << 1);
    build(mid + 1, r, (rt << 1) + 1);
    minn[rt] = min(minn[rt << 1], minn[(rt << 1) + 1]);
}
void update(int l, int r, int rt, int p) //單點更新
{
    if (p == l && r == p)
    {
        minn[rt] = 0x3f3f3f3f;
        return;
    }
    int mid = (l + r) >> 1;
    if (p <= mid)
        update(l, mid, rt << 1, p);
    else
        update(mid + 1, r, (rt << 1) + 1, p);
    minn[rt] = min(minn[rt << 1], minn[(rt << 1) + 1]);
}
int query(int l, int r, int rt, int l1, int r1, int v) //尋找從左到右第一個燈泡數
少於手上燈泡數的房間,若沒有則輸出-1
{
    if (l == r)
    {
        if (minn[rt] <= v)
            return l;
        return -1;
    }
    if (minn[rt] <= v)
    {
        int mid = (l + r) >> 1;
        int tmp = query(l, mid, rt << 1, l1, r1, v);
        if (tmp != -1)
            return tmp;
        tmp = query(mid + 1, r, (rt << 1) + 1, l1, r1, v);
        return tmp;
    }
    return -1;
}
int main()
{
//    freopen("in.txt", "r", stdin);
    scanf("%d%d", &n, &m);
    for (int i = 1; i <= n; ++i)
        scanf("%d", &tmp[i]);
    build(1, n, 1); //建樹
    cnt = 1;
    cntt = m;
    while (1)
    {
        int p = query(1, n, 1, 1, n, cntt);
        if (p == -1)
        {
            z[cnt].num = num;
            z[cnt++].keep = cntt;
            cntt += m;
            if (cnt >= maxn || num >= n)
                break;
            continue;
        }
        ++num; //記錄已經搞定的房間數
        cntt -= tmp[p]; //手上還有多少個燈泡
        update(1, n, 1, p);
    }
    for (int i = cnt; i < maxn; ++i) //後面多餘的月份都與最後一個月相同
        z[i] = z[i - 1];
    int t;
    scanf("%d", &t);
    while (t--)
    {
        int nn;
        scanf("%d", &nn);
        printf("%d %d\n", z[nn].num, z[nn].keep);
    }
    return 0;
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章