2016 CCSP ticket_chooser

#include <bits/stdc++.h>
#define N 300010
#define ll long long
using namespace std;
const ll INF = 1ll << 60;
int mp[200][200];

/**
 * 由于每次购票都是连续且要离中心点最近,所以售出的座位一定是相连的
 * 定义空白区域为:同一行中没有售出的连续座位
 * 定义空白区域的大小为:该空白区域的连续座位的数量
 * 推导公式可以发现每次只需要在所有大小大于m的空白区域中找到离中心点最近的点即可
 *
 * 购票时分为两种情况:
 * 第一种是选择所有座位都未售出的行进行购票,这种情况只需要选择纵向离中心点最近的即可。
 * 第二种是选择已经售出过票的行进行购票。
 *
 * 接下来详细介绍第二种情况。
 *
 * 对于已经售出过票的行来说,肯定被分成了三部分:左边未售票的空白区域,已经售票的区域,右边未售票的空白区域,
 * 而由于每次购票都要选择离中心点最近,所以左右两边未售票的空白区域大小一定小于等于k/2。
 *
 * 定义两个数组sl,sr,表示每一行左边空白区域和右边空白区域到中心点的横向距离。
 * 定义平衡树S数组,S[i]存储所有大小为i的空白区域的{到中心点的纵向距离,行号},
 * 由于S[i]中的空白区域的大小都相同,即距离中心点的横向距离相同,故只用到中心点的纵向距离作为第一关键字即可。
 *
 * 维护一颗线段树,下标表示空白区域端点到中心点的横向距离,值是一个pair,
 * 第一关键字表示在所有到中心点横向距离为下标的空白区域集合中端点到中心点的最小距离,
 * 第二关键字表示对应的行号
 *
 * 当我们需要购买m张票时,首先使用线段树查询到横向距离小于center-m这段区间的最小值得到行号,计算答案,
 * 接着我们需要去更新一下我们的数据结构:
 * 更新sl、sr,
 * 更新S,
 * 更新线段树
 */

/**
 * 线段树部分
 * 下标为区域到中心点的横向距离
 * 值是区域到中心点的距离
 */

int n, k, m, center;
struct SegTree
{
    int l, r;
    pair<ll, int> x;
} tr[N << 2];
void pushup(int u)
{
    tr[u].x = min(tr[u << 1].x, tr[u << 1 | 1].x);
}
void build(int u, int l, int r)
{
    tr[u] = {l, r};
    if (l == r)
    {
        tr[u].x = {INF, 0};
        return;
    }
    int mid = l + r >> 1;
    build(u << 1, l, mid), build(u << 1 | 1, mid + 1, r);
    pushup(u);
}
pair<ll, int> query(int u, int l, int r)
{
    if (l > r)
        return {INF, 0};
    if (tr[u].l >= l && tr[u].r <= r)
        return tr[u].x;
    int mid = tr[u].l + tr[u].r >> 1;
    pair<ll, int> res = {INF, 0};
    if (mid >= l)
        res = query(u << 1, l, r);
    if (mid < r)
        res = min(res, query(u << 1 | 1, l, r));
    return res;
}
void modify(int u, int r, ll x, int p)
{
    x = min(x, INF);
    if (r < tr[u].l || r > tr[u].r)
        return;
    if (tr[u].l == tr[u].r)
    {
        tr[u].x = {x, p};
        return;
    }
    int mid = tr[u].l + tr[u].r >> 1;
    if (mid >= r)
        modify(u << 1, r, x, p);
    else
        modify(u << 1 | 1, r, x, p);
    pushup(u);
}
int sl[N], sr[N];
set<pair<int, int>> S[N];

void update(int x)
{
    ll d;
    int r;
    if (S[x].size() == 0)
    {
        d = INF;
        r = 0;
    }
    else
    {
        d = (*S[x].begin()).first;
        r = (*S[x].begin()).second;
    }
    modify(1, x, d + x, r);
}

int cnt;
ll calc1(int row, int s, int l)
{
    return 1ll * l * abs(center - row) + 1ll * l * (s + s + l - 1) / 2;
}
ll calc2(int row, int l)
{
    return calc1(row, 0, l + 1 >> 1) + calc1(row, 1, l >> 1);
}
void printSeg()
{
    for (int i = 1; i < center; i++)
    {
        printf("%lld ", query(1, i, i).first);
    }
    printf("\n");
}
void solve()
{
    memset(mp, 0, sizeof(mp));
    memset(sl, 0, sizeof(int) * (k + 5));
    memset(sr, 0, sizeof(int) * (k + 5));
    center = k + 1 >> 1;
    for (int i = 1; i <= k; i++)
        S[i].clear();
    build(1, 1, center);
    cnt = 0;
    int row, L, R;
    for (int i = 1; i <= n; i++)
    {
        scanf("%d", &m);
        int r0 = query(1, 1, center - m).second, r1;
        if (cnt & 1)
            r1 = center - (cnt + 1 >> 1);
        else
            r1 = center + cnt / 2;
        // 计算答案
        int tmp = min(sl[r0], sr[r0]);
        ll val0 = calc1(r0, tmp, m);
        ll val1 = calc2(r1, m);
        if (r0 <= 0 || r0 > k)
            val0 = INF;
        if (r1 <= 0 || r1 > k)
            val1 = INF;
        if (val0 == INF && val1 == INF || m > k)
        {
            printf("-1\n");
            continue;
        }
        if (val0 < val1 || val0 == val1 && r0 < r1)
        {
            row = r0;
            if (sl[r0] <= sr[r0])
            {
                R = center - sl[r0], L = R - m + 1;
                // 更新sl
                sl[r0] += m;
            }
            else
            {
                L = center + sr[r0], R = L + m - 1;
                // 更新sr
                sr[r0] += m;
            }
            S[tmp].erase({abs(r0 - center), r0});
            update(tmp);
            tmp = min(sl[r0], sr[r0]);
            S[tmp].insert({abs(r0 - center), r0});
            update(tmp);
        }
        else
        {
            row = r1;
            L = center - m / 2, R = center + (m - 1) / 2;
            ++cnt;
            sl[r1] = center - L + 1, sr[r1] = R + 1 - center;
            tmp = sr[r1];
            S[tmp].insert({abs(r1 - center), r1});
            update(tmp);
        }
        printf("%d %d %d\n", row, L, R);
    }
}
int main()
{
    while (scanf("%d%d", &n, &k) == 2)
        solve();
    return 0;
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值