20221005多校联训
T1 CF1252G Performance Review
一个公司有 \(n\) 个员工,每个员工有一个能力值 \(a_i\), 每个 \(a_i\) 都不一样。公司采取末位淘汰制,我们考虑 \(m\) 年,每年年底都会补充 \(r_i\) 个新员工,每个新员工的能力值被表示为 \(b_{ij}\), 即第 \(i\) 年新加入的第 \(j\) 个员工的能力值。每年,能力值最靠后的 \(r_i\) 个员工会被炒鱿鱼,取而代之的是新加入的这 \(r_i\) 个员工。你是第 \(1\) 号员工,你的能力值是 \(a_1\) 。
还有 \(q\) 次操作,每次操作的描述形如 \((x_i,y_i,z_i)\), 表示把第 \(x_i\) 年加入的第 \(y_i\) 个员工的能力值改为 \(z_i\)。 现在对于每个操作输出 \(0\) 或 \(1\),表示如果进行完这个操作,\(m\) 年之后你会不会被炒鱿鱼。注意,操作是永久性的。
注意到,对于当前数列中的所有数,可以简单地将它们分为两类,即大于 \(a_1\) 的一类和小于 \(a_1\) 这一类。这两类数决定了 \(a_1\) 在当前数列中的排名,影响着它是否会被炒鱿鱼。
那么,我们只需要关注每个时刻数列中比 \(a_1\) 小的数的个数和当前时刻会炒多少人的鱿鱼的关系。如果前者小于后者,那么就会被炒鱿鱼。
题目中还有修改操作,对于第 \(x\) 年某个值的修改操作可能会影响到 \([x,m]\) 这个范围年份中比 \(a_1\) 小的数的数量。显然这是一个区间修改操作,考虑用线段树来维护 \(a_i\) 的排名。
对于一次修改,如果修改之前的数小于 \(a_1\),且修改后的数大于 \(a_1\),那么在 \(x\) 年后,比 \(a_1\) 小的数的数量都减一。反之,如果修改之前的数大于 \(a_1\),且修改之后的数小于 \(a_1\),那么在 \(x\) 年后,比 \(a_1\) 小的数的数量都加一。令第 \(k\) 年比 \(a_1\)小的数的数量为 \(p[k]\)。我们在线段树中还应该记录一个 \(p[k] - r[k]\) 的最小值 \(mn\)。如果在整个区间中,\(mn\) 的最小值小于 \(0\),那么你就会被开除,反之则不会。
代码:
#include<bits/stdc++.h>
#define ll (i<<1)
#define rr (i<<1) + 1
#define mid (tl + tr) / 2
using namespace std;
const int MAXN = 1e5;
int n,m,a[MAXN + 5],q;
vector<int> ins[MAXN + 5];
int cnt;
struct node{
int lazy,num,mn;
}tree[MAXN * 4 + 20];
void build(int i,int tl,int tr){
if(tl == tr){
tree[i].num = cnt;
return;
}
build(ll,tl,mid);
build(rr,mid + 1,tr);
}
void push_down(int i){
tree[ll].lazy += tree[i].lazy;
tree[rr].lazy += tree[i].lazy;
tree[ll].mn += tree[i].lazy;
tree[rr].mn += tree[i].lazy;
tree[i].lazy = 0;
}
void change(int i,int tl,int tr,int l,int r,int k){
if(tl > r || tr < l)return;
if(tl >= l && tr <= r){
tree[i].mn += k;
tree[i].lazy += k;
return;
}
push_down(i);
change(ll,tl,mid,l,r,k);
change(rr,mid + 1,tr,l,r,k);
tree[i].mn = min(tree[ll].mn,tree[rr].mn);
}
int b[MAXN + 5];
int query(int i,int tl,int tr,int p){
if(tl == tr){
tree[i].num += tree[i].lazy;
tree[i].lazy = 0;
tree[i].mn = tree[i].num - b[tl];
return tree[i].num;
}
push_down(i);
int ans;
if(p > mid)ans = query(rr,mid + 1,tr,p);
else ans = query(ll,tl,mid,p);
tree[i].mn = min(tree[ll].mn,tree[rr].mn);
}
int main(){
// freopen("a3.in","r",stdin);
// freopen("ans","w",stdout);
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
cin >> n >> m >> q;
for(int i = 1; i <= n; i++){
cin >> a[i];
if(i != 1 && a[i] < a[1])cnt++;
}
build(1,1,m);
for(int i = 1; i <= m;i++){
cin >> b[i];
int c;
cnt = 0;
for(int j = 1; j <= b[i];j++){
cin >> c;
ins[i].push_back(c);
if(c < a[1])cnt++;
}
int p = query(1,1,m,i);
c = cnt - b[i];
change(1,1,m,i + 1,m,c);
}
for(int i = 1; i <= m; i++)int p = query(1,1,m,i);
int x,y,z;
int now = 1;
for(int i = 1; i <= q; i++){
cin >> x >> y >> z;
if(ins[x][y - 1] < a[1] && z > a[1])change(1,1,m,x + 1,m,-1);
else if(ins[x][y - 1] > a[1] && z < a[1])change(1,1,m,x + 1,m,1);
bool flag = 0;
int k = query(1,1,m,1);
k = query(1,1,m,m);
if(tree[1].mn < 0)cout << "0\n";
else cout << "1\n";
ins[x][y - 1] = z;
}
}
//5 3 3
//50 40 30 20 10
//4 1 2 3 100
//1 4
//2 6 7
//1 3 300
//2 1 400
//2 1 52