题目链接:约会安排
大致题意
这个题… 全中文, 具体的题意麻烦大家看看题面吧.
解题思路
线段树的区间修改和区间查询.
线段树内需要维护当前区间内最大连续长度. 由于女神和基友的查询是有区别的, 所以要分别记录基友和女神的两种情况.
分析题目需要的操作:
①基友和自己约会, 那么需要查看当前区间是否有. 如果有则进行修改.
②女生和自己约会, 先在基友树中寻找是否有时间, 如果没有再在女神树中查找. 若找到则进行区间修改.
③自己想好好学习了, 则清空基友树和女神树的[l, r]区间
对于操作①和②, 我们应到找到满足要求的区间的左端点L, 然后修改[L, L + x - 1]即可.
由于这个题需要区别对待基友和女神, 我们可以考虑开两颗线段树来实现所有操作, 也可以考虑在一个线段树中维护两类信息. 思路都是差不多的.
特别注意的是, 对于女神树的所有操作, 也都会对基友树有同样的操作.
AC代码
/* 一颗线段树维护两类信息 */
#include <bits/stdc++.h>
#define rep(i, n) for (int i = 1; i <= (n); ++i)
using namespace std;
typedef long long ll;
const int N = 1E5 + 10;
struct node {
int l, r;
int fmax[2], lmax[2], rmax[2]; //0为基友 1为女神
int lazy[2];
}t[N << 2];
void pushdown(int id, node& op, int lazy) { //id用于区分基友和女神
op.fmax[0] = (op.r - op.l + 1) * lazy;
op.lmax[0] = op.rmax[0] = op.fmax[0];
op.lazy[0] = lazy;
if (id == 1) {
op.fmax[1] = op.lmax[1] = op.rmax[1] = op.fmax[0];
op.lazy[1] = op.lazy[0];
}
}
void pushdown(int x) {
if (t[x].lazy[1] != -1) pushdown(1, t[x << 1], t[x].lazy[1]), pushdown(1, t[x << 1 | 1], t[x].lazy[1]);
if (t[x].lazy[0] != -1) pushdown(0, t[x << 1], t[x].lazy[0]), pushdown(0, t[x << 1 | 1], t[x].lazy[0]);
t[x].lazy[0] = t[x].lazy[1] = -1;
}
void pushup(int x) {
node& p = t[x], &l = t[x << 1], &r = t[x << 1 | 1];
for (int i = 0; i < 2; ++i) {
p.fmax[i] = max(max(l.fmax[i], r.fmax[i]), l.rmax[i] + r.lmax[i]);
p.lmax[i] = l.lmax[i] + (l.lmax[i] == l.r - l.l + 1 ? r.lmax[i] : 0);
p.rmax[i] = r.rmax[i] + (r.rmax[i] == r.r - r.l + 1 ? l.rmax[i] : 0);
}
}
void build(int l, int r, int x = 1) {
t[x] = { l, r, 1, 1, 1, 1, 1, 1, -1, -1 };
if (l == r) return;
int mid = l + r >> 1;
build(l, mid, x << 1), build(mid + 1, r, x << 1 | 1);
pushup(x);
}
void modify(int l, int r, int id, int c, int x = 1) {
if (l <= t[x].l && r >= t[x].r) {
pushdown(id, t[x], c);
return;
}
pushdown(x);
int mid = t[x].l + t[x].r >> 1;
if (l <= mid) modify(l, r, id, c, x << 1);
if (r > mid) modify(l, r, id, c, x << 1 | 1);
pushup(x);
}
int findleft(int l, int r, int id, int c, int x = 1) { //返回至少有c连续空闲时间的区间左端点
if (t[x].l == t[x].r) return t[x].l;
pushdown(x);
if (t[x << 1].fmax[id] >= c) return findleft(l, r, id, c, x << 1);
if (t[x << 1].rmax[id] + t[x << 1 | 1].lmax[id] >= c) return t[x << 1].r - t[x << 1].rmax[id] + 1;
return findleft(l, r, id, c, x << 1 | 1);
}
int main()
{
int T; cin >> T;
rep(CASE, T) {
printf("Case %d:\n", CASE);
int n, m; scanf("%d %d", &n, &m);
build(1, n);
while (m--) {
char s[10]; scanf("%s", s);
if (*s == 'S') {
int l, r; scanf("%d %d", &l, &r);
modify(l, r, 1, 1);
printf("I am the hope of chinese chengxuyuan!!\n");
}
else {
int c; scanf("%d", &c);
if (*s == 'N') {
int id = -1;
if (t[1].fmax[0] >= c) id = 0;
else if (t[1].fmax[1] >= c) id = 1;
if (id != -1) {
int l = findleft(1, n, id, c);
modify(l, l + c - 1, 1, 0);
printf("%d,don't put my gezi\n", l);
}
else printf("wait for me\n");
}
else if (*s == 'D') {
if (t[1].fmax[0] >= c) {
int l = findleft(1, n, 0, c);
modify(l, l + c - 1, 0, 0);
printf("%d,let's fly\n", l);
}
else printf("fly with yourself\n");
}
}
}
}
return 0;
}
/* 两颗线段树维护方式 */
#include <bits/stdc++.h>
#define rep(i, n) for (int i = 1; i <= (n); ++i)
using namespace std;
typedef long long ll;
const int N = 1E5 + 10;
struct node {
int l, r;
int fmax, lmax, rmax;
int lazy;
}t1[N << 2], t2[N << 2]; // 基友, 女神
void pushdown(node& op, int lazy) {
op.fmax = lazy * (op.r - op.l + 1);
op.lmax = op.fmax, op.rmax = op.fmax;
op.lazy = lazy;
}
void pushdown(node t[], int x) {
if (t[x].lazy == -1) return;
pushdown(t[x << 1], t[x].lazy), pushdown(t[x << 1 | 1], t[x].lazy);
t[x].lazy = -1;
}
void pushup(node& p, node& l, node& r) {
p.fmax = max(max(l.fmax, r.fmax), l.rmax + r.lmax);
p.lmax = l.lmax + (l.lmax == l.r - l.l + 1 ? r.lmax : 0);
p.rmax = r.rmax + (r.rmax == r.r - r.l + 1 ? l.rmax : 0);
}
void pushup(node t[], int x) { pushup(t[x], t[x << 1], t[x << 1 | 1]); }
void build(int l, int r, int x = 1) {
t1[x] = t2[x] = { l, r, 1, 1, 1, -1 };
if (l == r) return;
int mid = l + r >> 1;
build(l, mid, x << 1), build(mid + 1, r, x << 1 | 1);
pushup(t1, x), pushup(t2, x);
}
int findleft(node t[], int c, int x = 1) { //找到当前树中最左侧有连续c空闲时间的左端点(起始点)
if (t[x].l == t[x].r) return t[x].l;
pushdown(t, x);
if (t[x << 1].fmax >= c) return findleft(t, c, x << 1);
if (t[x << 1].rmax + t[x << 1 | 1].lmax >= c) return t[x << 1].r - t[x << 1].rmax + 1;
return findleft(t, c, x << 1 | 1);
}
void modify(node t[], int l, int r, int x = 1) { //修改某一课树的
if (l <= t[x].l && r >= t[x].r) { pushdown(t[x], 0); return; }
pushdown(t, x);
int mid = t[x].l + t[x].r >> 1;
if (l <= mid) modify(t, l, r, x << 1);
if (r > mid) modify(t, l, r, x << 1 | 1);
pushup(t, x);
}
void modify(int l, int r, int c, int x = 1) { //两棵树一起修改的
if (l <= t1[x].l && r >= t1[x].r) {
pushdown(t1[x], c), pushdown(t2[x], c);
return;
}
pushdown(t1, x), pushdown(t2, x);
int mid = t1[x].l + t1[x].r >> 1;
if (l <= mid) modify(l, r, c, x << 1);
if (r > mid) modify(l, r, c, x << 1 | 1);
pushup(t1, x), pushup(t2, x);
}
int main()
{
int T; cin >> T;
rep(Case, T) {
printf("Case %d:\n", Case);
int n, m; scanf("%d %d", &n, &m);
build(1, n);
while (m--) {
char s[10]; scanf("%s", s);
if (*s == 'S') {
int l, r; scanf("%d %d", &l, &r);
modify(l, r, 1);
printf("I am the hope of chinese chengxuyuan!!\n");
}
else {
int x; scanf("%d", &x);
if (*s == 'N') {
if (t1[1].fmax >= x) { //基友树有足够的时间
int l = findleft(t1, x);
modify(l, l + x - 1, 0);
printf("%d,don't put my gezi\n", l);
}
else if (t2[1].fmax >= x){ //女神树有足够的时间
int l = findleft(t2, x);
modify(l, l + x - 1, 0);
printf("%d,don't put my gezi\n", l);
}
else printf("wait for me\n");
}
else if (*s == 'D') {
if (t1[1].fmax >= x) {
int l = findleft(t1, x);
modify(t1, l, l + x - 1);
printf("%d,let's fly\n", l);
}
else printf("fly with yourself\n");
}
}
}
}
return 0;
}
这个题要注意题目中的字符, 都需要是英文字符, 如果你怕粘贴错误, 不妨直接粘贴本文代码的输出语句.
(据听说原题中是有中文的字符的, 恐怖如斯QAQ)