来源:The 2021 ICPC Asia Regionals Online Contest (I)
题目大意:第一行给两个数n,m,表示有n个结点(编号为0,1,2,…)和m个任务。
接下来i行(i=0,1,2,…,m-1)每行给出两个数,分别为任务的开始时间和持续时间。接着从i%m开始向右找第一个空闲的点,如果没有空闲的点就跳过。最后输出接任务最多的点,如果有多个并列的按升序输出。
思路:每个结点给一个结束任务时的时间,然后用线段树维护,对于每个任务先查询左侧是否有空闲,如果有进入左侧接着查询,如果没有进入右侧,保证找到最靠近左侧的空闲点。这题对时间卡得比较死,在一些细节方面还需要稍微优化才能过。
//time:173ms
#include <bits/stdc++.h>
#define MAXN 100005
#define lson rt << 1
#define rson rt << 1 | 1
using namespace std;
int tree[MAXN * 4] = {0};
int nn, cnt[MAXN] = {0};
int query(int ql, int qr, int rt = 1, int l = 0, int r = nn)
{
if (ql <= l && qr >= r)
return tree[rt];
int mid = (l + r) >> 1;
if (qr <= mid)
return query(ql, qr, lson, l, mid);
if (ql > mid)
return query(ql, qr, rson, mid + 1, r);
return min(query(ql, qr, lson, l, mid), query(ql, qr, rson, mid + 1, r));
}
void update(int x, int val, int rt = 1, int l = 0, int r = nn)
{
if (l == r)
{
tree[rt] = val;
return;
}
int mid = (l + r) >> 1;
if (x <= mid)
update(x, val, lson, l, mid);
else
update(x, val, rson, mid + 1, r);
tree[rt] = min(tree[lson], tree[rson]);
}
int check(int now, int x) //查询现在是否有空闲的点
{
if (tree[1] > now)
return -1;
int l, r, mid;
if (query(x, nn) <= now)
l = x, r = nn;
else
l = 0, r = x - 1;
while (l < r)
{
mid = (l + r) >> 1;
if (query(l, mid) <= now)
r = mid;
else
l = mid + 1;
}
return l;
}
int main()
{
int s, cost, n, m;
scanf("%d%d", &n, &m);
nn = n - 1;
for (int i = 0; i < m; i++)
{
int ii = i % n;
scanf("%d%d", &s, &cost);
int sub = check(s, ii);
if (sub >= 0)
{
update(sub, s + cost);
cnt[sub]++;
}
}
vector<int> ans;
int maxcnt = 0;
for (int i = 0; i < n; ++i)
maxcnt = max(maxcnt, cnt[i]);
for (int i = 0; i < n; i++)
if (cnt[i] == maxcnt)
ans.push_back(i);
for (int i = 0; i < ans.size() - 1; i++)
printf("%d ", ans[i]);
if (ans.size() > 0)
printf("%d", ans.back());
return 0;
}