题目链接:点击查看
题目大意:在一个公司中初始时有 n 个人,每个人都有一个能力值,题目保证每个能力值都不相等,第 i 天会开除能力值最低的 r[ i ] 个人,并且会新加入 r[ i ] 个人,分别是 b[ i ][ 1 ] ~ b[ i ][ r[ i ] ],现在有 q 次询问,每次询问会给出三个参数:x , y , z ,意思就是将 b[ x ][ y ] 改成 z 后,第一个人最后会不会开除,会被开除的话输出 0 ,否则输出 1
题目分析:看起来是需要维护一个排过序的关系,但实际上我们只需要维护一下相对的大小关系即可,因为题目中保证了能力值互不相同,所以我们可以将除了第一个人以外的人分为两种人:(下面我都会将第一个人称之为目标)
- 能力值大于目标的人
- 能力值小于目标的人
两种方向都可以解决这个问题,这里我选择的是维护能力值小于目标的人
已知第 i 天会开除的人:r[ i ] 个能力值最低的人,又可以通过新加入的 r[ i ] 个人求出第 i 天新加入的人中有多少个人小于目标,记为 lower[ i ] ,最开始还没有操作的时候,也就是初始时 n 个人中小于目标的人数记为 lower[ 0 ] 即可
这样一来,对于每一天来说,我们只需要保证对于每一天来说 ( 能力值小于目标的人数 ) >= ( 被开除的人数 ) 全部成立,移个项就是 ( 能力值小于目标的人数 ) - ( 被开除的人数 ) >= 0 ,不难发现被开除的人数是一个定值,也就是 r[ i ] ,而能力值小于目标的人数,这个可以通过前一项迭代计算出,我们给表达式赋予意义,那么 ( 能力值小于目标的人数 ) - ( 被开除的人数 ) 的意义就是开除员工后,公司内剩下的人,再加上一项变成 ( 能力值小于目标的人数 ) - ( 被开除的人数 ) + ( 新增加人数中小于目标的人数 ),此时这个表达式的意义就变为了当前公司内有多少个能力值小于目标的人数(能力值大于目标的人数显然不起作用,忽略即可),这样我们可以求出最开始需要求的迭代式:
ans[ i ] = ans[ i - 1 ] + lower[ i - 1 ] - r[ i ]
那么对于某次修改会造成什么影响呢,考虑将 b[ x ][ y ] 变为 z ,那么只会让第 x 位上的 lower[ x ] 加一或者减一,不难发现需要维护的 ans 数组是一个前缀和,对 ans 数组造成的影响就是 [ x + 1 ~ n ] 个位置都需要加一或者减一,这样就可以将前缀和放到线段树上维护了,维护一下区间最小值用来判断就可以了
代码:
#include<iostream>
#include<cstdio>
#include<string>
#include<ctime>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<stack>
#include<climits>
#include<queue>
#include<map>
#include<set>
#include<sstream>
#include<cassert>
#include<bitset>
#include<unordered_map>
using namespace std;
typedef long long LL;
typedef unsigned long long ull;
const int inf=0x3f3f3f3f;
const int N=1e5+100;
int lower[N],a[N];
vector<int>b[N];
struct Node
{
int l,r;
int mmin;
int lazy;
}tree[N<<2];
void pushup(int k)
{
tree[k].mmin=min(tree[k<<1].mmin,tree[k<<1|1].mmin);
}
void pushdown(int k)
{
if(tree[k].lazy)
{
int lz=tree[k].lazy;
tree[k].lazy=0;
tree[k<<1].mmin+=lz;
tree[k<<1|1].mmin+=lz;
tree[k<<1].lazy+=lz;
tree[k<<1|1].lazy+=lz;
}
}
void build(int k,int l,int r)
{
tree[k].l=l;
tree[k].r=r;
tree[k].lazy=0;
if(l==r)
{
tree[k].mmin=lower[l];
return;
}
int mid=l+r>>1;
build(k<<1,l,mid);
build(k<<1|1,mid+1,r);
pushup(k);
}
void update(int k,int l,int r,int val)
{
if(l>r)
return;
if(tree[k].r<l||tree[k].l>r)
return;
if(tree[k].l>=l&&tree[k].r<=r)
{
tree[k].mmin+=val;
tree[k].lazy+=val;
return;
}
pushdown(k);
update(k<<1,l,r,val);
update(k<<1|1,l,r,val);
pushup(k);
}
int main()
{
#ifndef ONLINE_JUDGE
// freopen("data.in.txt","r",stdin);
// freopen("data.out.txt","w",stdout);
#endif
// ios::sync_with_stdio(false);
int n,m,q;
scanf("%d%d%d",&n,&m,&q);
for(int i=1;i<=n;i++)
{
scanf("%d",a+i);
if(a[i]<a[1])
lower[0]++;
}
int pre=0;//上一次加个多少个比a[1]小的数有多少
for(int i=1;i<=m;i++)
{
int num;
scanf("%d",&num);
lower[i]=lower[i-1]+pre-num;
pre=0;
b[i].push_back(-1);
while(num--)
{
int x;
scanf("%d",&x);
b[i].push_back(x);
if(x<a[1])
pre++;
}
}
build(1,1,m);
while(q--)//lower的公式:lower[i]=lower[i-1]+pre-num,num是不变的,变的是pre
{
int x,y,z;
scanf("%d%d%d",&x,&y,&z);
if(b[x][y]>a[1]&&a[1]>z)//改变之后第x位置的pre->pre+1
update(1,x+1,m,1);
if(b[x][y]<a[1]&&a[1]<z)//改变之后第x位置的pre->pre-1
update(1,x+1,m,-1);
b[x][y]=z;
if(tree[1].mmin<0)
puts("0");
else
puts("1");
}
return 0;
}