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;
}

 

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