#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;
}
2016 CCSP ticket_chooser
最新推荐文章于 2024-06-12 10:00:23 发布