题解:P11219 【MX-S4-T3】「yyOI R2」youyou 的序列 II
思路
先提出一个结论(以下讨论均局限于询问的区间
令第二个人可以操作的区间集合为
如果存在一个区间
-
第一个人可以操作
(L,R) ,即R-L+1\le c_1,\sum_{i=l}^r\le w_1 。 -
并且如果忽略第二个人的存在,这个区间可以全部变成红色,即
如果以上两个条件均满足,那么第一个人会赢。否则第二个人会赢。
证明
先证充分性:充分性是显然的。第一个人只需要先把
然后证必要性:考虑反证。假设不存在区间
设
假设第一个人操作了区间
-
若
l_3\le l_1\le r_3 ,即操作影响了l_1 ,那么操作区间(l_1,r_1) ,使l_1 变回蓝色。 -
若
l_3\le r_2\le r_3 ,即操作影响了r_2 ,那么操作区间(l_2,r_2) ,使r_2 变回蓝色。 -
否则,可以任意进行操作。
因为第一个人的可操作区间中不存在
于是我们证明了这个结论。于是我们可以对于每次询问,找到集合
容易发现,当且仅当第一个人可以操作
那么如何寻找
可以维护一个值
那么,
当然,如果
总结一下,具体的步骤就是:
-
先判一下
\max_{i=x}^ya_i\le w_1 是否成立,若不成立直接输出tetris。 -
然后判断
y-x+1 与c_2 的大小关系。如果y-x+1<c_2 ,直接判断\sum_{i=x}^ya_i>w_2 是否成立,若成立就令l_1=x,r_2=y ,否则输出cont(因为这时第二个人无法操作)。 -
如果
y-x+1\le c_2 ,就用之前讲的方法求(l_1,r_2) 。 -
最后判断第一个人能否操作
(l_1,r_2) ,即r_2-l_1+1\le c_1,\sum_{i=l_1}^{r_2}a_i\le w_1 是否成立。若成立则输出cont,否则输出tetris。
然后就做完了。修改使用线段树、树状数组容易维护。时间复杂度
代码
#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn = 3e5 + 5;
int n, q, c1, c2;
ll w1, w2;
ll a[maxn], tr[maxn];
void upd(int id, ll k){
for(int i = id; i <= n; i += i & -i) tr[i] += k;
}
ll que(int id){
ll s = 0;
for(int i = id; i > 0; i -= i & -i) s += tr[i];
return s;
}
namespace seg{
#define l(x) (x << 1)
#define r(x) (x << 1 | 1)
ll max1[maxn << 2], tag[maxn << 2];
void up(int x){
max1[x] = max(max1[l(x)], max1[r(x)]);
}
void down(int x){
max1[l(x)] += tag[x], tag[l(x)] += tag[x];
max1[r(x)] += tag[x], tag[r(x)] += tag[x];
tag[x] = 0;
}
void update(int x, int l, int r, int ql, int qr, ll k){
if(ql <= l && r <= qr){
max1[x] += k, tag[x] += k;
return;
}
down(x);
int mid = l + r >> 1;
if(ql <= mid) update(l(x), l, mid, ql, qr, k);
if(qr > mid) update(r(x), mid + 1, r, ql, qr, k);
up(x);
}
int query1(int x, int l, int r, int ql, int qr, ll k){
if(ql <= l && r <= qr){
if(max1[x] <= k) return 0;
if(l == r){
if(max1[x] > k) return l;
else return 0;
}
down(x);
int mid = l + r >> 1;
if(max1[l(x)] > k) return query1(l(x), l, mid, ql, qr, k);
else return query1(r(x), mid + 1, r, ql, qr, k);
}
down(x);
int mid = l + r >> 1, res = 0;
if(ql <= mid) res = query1(l(x), l, mid, ql, qr, k);
if(res) return res;
if(qr > mid) res = query1(r(x), mid + 1, r, ql, qr, k);
return res;
}
int query2(int x, int l, int r, int ql, int qr, ll k){
if(ql <= l && r <= qr){
if(max1[x] <= k) return 0;
if(l == r){
if(max1[x] > k) return l;
else return 0;
}
down(x);
int mid = l + r >> 1;
if(max1[r(x)] > k) return query2(r(x), mid + 1, r, ql, qr, k);
else return query2(l(x), l, mid, ql, qr, k);
}
down(x);
int mid = l + r >> 1, res = 0;
if(qr > mid) res = query2(r(x), mid + 1, r, ql, qr, k);
if(res) return res;
if(ql <= mid) res = query2(l(x), l, mid, ql, qr, k);
return res;
}}
namespace seg2{
#define l(x) (x << 1)
#define r(x) (x << 1 | 1)
ll max1[maxn << 2];
void up(int x){
max1[x] = max(max1[l(x)], max1[r(x)]);
}
void build(int x, int l, int r){
if(l == r){
max1[x] = a[l];
return;
}
int mid = l + r >> 1;
build(l(x), l, mid), build(r(x), mid + 1, r);
up(x);
}
void update(int x, int l, int r, int id, ll k){
if(l == r){
max1[x] += k;
return;
}
int mid = l + r >> 1;
if(id <= mid) update(l(x), l, mid, id, k);
else update(r(x), mid + 1, r, id, k);
up(x);
}
ll query(int x, int l, int r, int ql, int qr){
if(ql <= l && r <= qr) return max1[x];
int mid = l + r >> 1;
ll res = 0;
if(ql <= mid) res = max(res, query(l(x), l, mid, ql, qr));
if(qr > mid) res = max(res, query(r(x), mid + 1, r, ql, qr));
return res;
}}
int main(){
scanf("%d %d %d %d %lld %lld", &n, &q, &c1, &c2, &w1, &w2);
for(int i = 1; i <= n; i ++) scanf("%lld", &a[i]);
for(int i = 1; i <= n; i ++){
upd(i, a[i]);
seg::update(1, 1, n, max(1, i - c2 + 1), i, a[i]);
}
seg2::build(1, 1, n);
while(q --){
int op;
scanf("%d", &op);
if(op == 1){
int x;
ll y;
scanf("%d %lld", &x, &y);
upd(x, y);
seg::update(1, 1, n, max(1, x - c2 + 1), x, y);
seg2::update(1, 1, n, x, y);
a[x] += y;
}else{
int l, r;
scanf("%d %d", &l, &r);
if(seg2::query(1, 1, n, l, r) > w1){
printf("tetris\n");
continue;
}
int L = 0, R = 0;
if(r - l + 1 <= c2){
if(que(r) - que(l - 1) > w2) L = l, R = r;
}else{
L = seg::query1(1, 1, n, l, r - c2 + 1, w2);
R = seg::query2(1, 1, n, l, r - c2 + 1, w2) + c2 - 1;
}
if(!L || !R){
printf("cont\n");
continue;
}
if(que(R) - que(L - 1) <= w1 && R - L + 1 <= c1) printf("cont\n");
else printf("tetris\n");
}
}
return 0;
}