2021牛客暑期多校训练营6
F-Hamburger Steak
思路
按照时间长短降序排序,并计算 a n s b e s t = m a x ( ⌈ a v g ( t i ) ⌉ , m a x { t i } ) ans_{best} = max(\lceil avg(t_i) \rceil , max\{t_i\}) ansbest=max(⌈avg(ti)⌉,max{ti}) ,之后按照时间降序安排锅中汉堡的时间,若当前锅时间已经超过 a n s b e s t ans_{best} ansbest ,就换到下一锅进行安排并截掉超出的那部分放入下一锅,这样可以完全满足题意(汉堡不能同时放在两个锅里)并且时间最短。
代码
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int INF = 0x3f3f3f3f;
const int N = 1e5 + 10;
const int mod = 998244353;
struct node
{
ll l, r;
int id;
bool operator < (const node& obj) const
{
return l < obj.l;
}
};
struct node1
{
int id;
ll num;
bool operator < (const node1& obj) const
{
return num > obj.num;
}
};
node1 t[N];
vector<node> vec[N];
int main()
{
int n, m;
scanf("%d%d", &n, &m);
ll sum = 0;
for(int i = 0; i < n; i++)
{
scanf("%lld", &t[i].num);
t[i].id = i;
sum += t[i].num;
}
ll best = (sum + m - 1) / m;
sort(t, t + n);
if(t[0].num > best)
best = t[0].num;
int id = 1;
ll cnt = 0;
for(int i = 0; i < n; i++)
{
if(cnt + t[i].num > best)
{
vec[t[i].id].push_back({cnt, best, id});
t[i].num = t[i].num - best + cnt;
cnt = 0;
id++;
i--;
}
else
{
vec[t[i].id].push_back({cnt, cnt + t[i].num, id});
cnt += t[i].num;
if(cnt == best)
{
cnt = 0;
id++;
}
}
}
for(int i = 0; i < n; i++)
{
node t1, t2;
if(vec[i].size() == 1)
{
t1 = vec[i][0];
cout << 1 << ' ';
cout << t1.id << ' ' << t1.l << ' ' << t1.r << endl;
}
else
{
t1 = vec[i][0];
t2 = vec[i][1];
cout << 2 << ' ';
if(t2.l > t1.l)
{
cout << t1.id << ' ' << t1.l << ' ' << t1.r << ' ';
cout << t2.id << ' ' << t2.l << ' ' << t2.r << endl;
}
else
{
cout << t2.id << ' ' << t2.l << ' ' << t2.r << ' ';
cout << t1.id << ' ' << t1.l << ' ' << t1.r << endl;
}
}
}
return 0;
}
H-Hopping Rabbit
思路
其实从 ( x , y ) (x, y) (x,y) 点出发和从 ( x + d , y + d ) (x + d, y + d) (x+d,y+d) 出发是一样的,因为左右都只能跳 d d d 的距离,那么有区别的出发点其实都可以归纳到 x ∈ [ 0 , d ) , y ∈ [ 0 , d ) x \in [0, d), y \in [0, d) x∈[0,d),y∈[0,d) 的范围内。我们把所有的陷阱都通过这种方法映射到 x ∈ [ 0 , d ) , y ∈ [ 0 , d ) x \in [0, d), y \in [0, d) x∈[0,d),y∈[0,d) 的范围内(赋值为1),然后找任意一个值为0的点输出其坐标即可,若没有这样的点就是不可行。
听起来很简单,但是d的范围1e5,暴力显然不行。开始觉得用二维线段树也可以做,奈何不会,后来在想“什么数据结构可以用来处理矩形覆盖问题”的时候,突然想到扫描线。扫描线扫的过程中查找是不是有某一列的值不是满的,若有就说明该列有0,若无则没有答案。若找到了某个有0的列,就将其之前的所有线段开头结尾在一个初始为0的数组中标记(开头+1,结尾-1),最后 O ( n ) O(n) O(n) 求一遍前缀和就可以知道在修改到该列时,整列的状态,此时若有前缀和为0的位置,就是我们要找的答案。这样的复杂度是 O ( log d + d ) O(\log d + d) O(logd+d) ,是可以通过的。
不过好像题目数据有点弱,我们漏判了开头到第一个矩形的部分,居然过了。
代码
赛后自己重新写了一遍,完美复现了赛场上的所有错误(不是)
被负数取模坑了几个小时,好像这个细节还是我在赛场上提出来的,转头就忘不愧是我()
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1e5 + 10;
int n;
struct Segment
{
ll x, y1, y2;
int k;
bool operator < (const Segment& t) const
{
return x < t.x;
}
} seg[N * 8];
struct node
{
int l, r;
ll cnt;
ll len;
} tr[N * 16];
vector<int> ys;
int find(int y)
{
return lower_bound(ys.begin(), ys.end(), y) - ys.begin();
}
void pushup(int u)
{
if(tr[u].cnt)
tr[u].len = ys[tr[u].r + 1] - ys[tr[u].l];
else if(tr[u].l != tr[u].r)
tr[u].len = tr[u << 1].len + tr[u << 1 | 1].len;
else
tr[u].len = 0;
}
void build(int u, int l, int r)
{
tr[u] = {l, r, 0, 0};
if(l != r)
{
int mid = (l + r) >> 1;
build(u << 1, l, mid);
build(u << 1 | 1, mid + 1, r);
}
}
void modify(int u, int l, int r, int k)
{
if(tr[u].l >= l && tr[u].r <= r)
{
tr[u].cnt += k;
pushup(u);
}
else
{
int mid = (tr[u].l + tr[u].r) >> 1;
if(l <= mid)
modify(u << 1, l, r, k);
if(r > mid)
modify(u << 1 | 1, l, r, k);
pushup(u);
}
}
int f[N];
int main()
{
int n, d, j = 0;
scanf("%d%d", &n, &d);
bool flag = 0;
seg[j++] = {0, 0, d, 0};
seg[j++] = {d, 0, d, 0};
ll add = 1e9 * d;
for(int i = 0; i < n; i++)
{
ll x1, y1, x2, y2;
scanf("%lld%lld%lld%lld", &x1, &y1, &x2, &y2);
if(x2 - x1 >= d && y2 - y1 >= d)
{
flag = 1;
break;
}
x1 += add;
x2 += add;
y1 += add;
y2 += add;
x1 %= d;
y1 %= d;
x2 = (x2 - 1 + d) % d + 1;
y2 = (y2 - 1 + d) % d + 1;
if(x1 < x2)
{
if(y1 < y2)
{
seg[j++] = {x1, y1, y2, 1};
seg[j++] = {x2, y1, y2, -1};
ys.push_back(y1);
ys.push_back(y2);
}
else if(y1 > y2) // [y1, d) + [0, y2)
{
seg[j++] = {x1, y1, d, 1};
seg[j++] = {x2, y1, d, -1};
seg[j++] = {x1, 0, y2, 1};
seg[j++] = {x2, 0, y2, -1};
ys.push_back(y1);
ys.push_back(y2);
ys.push_back(0);
ys.push_back(d);
}
else
{
seg[j++] = {x1, 0, d, 1};
seg[j++] = {x2, 0, d, -1};
ys.push_back(0);
ys.push_back(d);
}
}
else if(x1 > x2)
{
if(y1 < y2) // [x1, d) + [0, x2)
{
seg[j++] = {x1, y1, y2, 1};
seg[j++] = {d, y1, y2, -1};
seg[j++] = {0, y1, y2, 1};
seg[j++] = {x2, y1, y2, -1};
ys.push_back(y1);
ys.push_back(y2);
}
else if(y1 > y2) // [x1, d) + [0, x2) + [y1, d) + [0, y2)
{
seg[j++] = {x1, y1, d, 1};
seg[j++] = {d, y1, d, -1};
seg[j++] = {0, y1, d, 1};
seg[j++] = {x2, y1, d, -1};
seg[j++] = {x1, 0, y2, 1};
seg[j++] = {d, 0, y2, -1};
seg[j++] = {0, 0, y2, 1};
seg[j++] = {x2, 0, y2, -1};
ys.push_back(y1);
ys.push_back(y2);
ys.push_back(0);
ys.push_back(d);
}
else
{
seg[j++] = {x1, 0, d, 1};
seg[j++] = {d, 0, d, -1};
seg[j++] = {0, 0, d, 1};
seg[j++] = {x2, 0, d, -1};
ys.push_back(0);
ys.push_back(d);
}
}
else
{
if(y1 < y2)
{
seg[j++] = {0, y1, y2, 1};
seg[j++] = {d, y1, y2, -1};
ys.push_back(y1);
ys.push_back(y2);
}
else if(y1 > y2) // [y1, d) + [0, y2)
{
seg[j++] = {0, y1, d, 1};
seg[j++] = {d, y1, d, -1};
seg[j++] = {0, 0, y2, 1};
seg[j++] = {d, 0, y2, -1};
ys.push_back(y1);
ys.push_back(y2);
ys.push_back(0);
ys.push_back(d);
}
else
{
flag = 1;
break;
}
}
}
if(flag)
{
printf("NO\n");
return 0;
}
int cnt = j;
sort(seg, seg + cnt);
sort(ys.begin(), ys.end());
ys.erase(unique(ys.begin(), ys.end()), ys.end());
build(1, 0, ys.size() - 2);
int res = 0;
int pos = -1;
for(int i = 0; i < cnt; i++)
{
if(i > 0)
{
int h = seg[i].x - seg[i - 1].x;
int w = tr[1].len;
if(h && w < d)
{
pos = i;
break;
}
if(seg[i].k)
modify(1, find(seg[i].y1), find(seg[i].y2) - 1, seg[i].k);
}
}
if(pos == -1)
{
printf("NO\n");
return 0;
}
for(int i = 0; i < pos; i++)
{
f[seg[i].y1] += seg[i].k;
f[seg[i].y2] -= seg[i].k;
}
int y = -1;
for(int i = 0; i < d; i++)
{
if(i > 0)
f[i] = f[i - 1] + f[i];
if(f[i] == 0)
{
y = i;
break;
}
}
if(y == -1)
{
printf("NO\n");
return 0;
}
int x = seg[pos - 1].x;
printf("YES\n%d %d\n", x, y);
return 0;
}
I-Intervals on the Ring
思路
设要凑出的集合为 [ l 1 , r 1 ] , [ l 2 , r 2 ] , … , [ l n , r n ] {[l_1, r_1], [l_2, r_2], \dots, [l_n, r_n]} [l1,r1],[l2,r2],…,[ln,rn] ,则答案序列为 [ l 1 , r n ] , [ l 2 , r 1 ] , [ l 3 , r 2 ] , … , [ l n , r n − 1 ] {[l_1, r_n], [l_2, r_1], [l_3, r_2], \dots, [l_n, r_{n - 1}]} [l1,rn],[l2,r1],[l3,r2],…,[ln,rn−1] 。
注意输入可能会有一个 l > r l > r l>r 的情况,这个要分两个处理。
显然是不会不存在的。
代码
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int INF = 0x3f3f3f3f;
const int N = 1e6 + 10;
const int mod = 998244353;
struct node
{
int l, r;
bool operator < (const node& obj) const
{
return l < obj.l;
}
};
vector<node> vec;
int main()
{
int T;
scanf("%d", &T);
while(T--)
{
vec.clear();
int n, m;
scanf("%d%d", &n, &m);
for(int i = 0, l, r; i < m; i++)
{
scanf("%d%d", &l, &r);
if(l > r)
{
vec.push_back({l, n});
vec.push_back({1, r});
}
else
{
vec.push_back({l, r});
}
}
sort(vec.begin(), vec.end());
printf("%d\n", vec.size());
printf("%d %d\n", vec[0].l, vec[vec.size() - 1].r);
for(int i = 1; i < vec.size(); i++)
{
printf("%d %d\n", vec[i].l, vec[i - 1].r);
}
}
return 0;
}