题目分析
首先二分答案,就可以只判断这几条可不可行了。
根据每个人的称述,我们先可以给这些人确定一个大致的“必须存在的区间”,而那些没有称述的人,根据boshi命名法,称其为“幽灵”。
假设当前检查的时刻 i i i,必须存在的人数为 s i s_i si。
nows
:由于“必须存在的区间”,而导致当前时刻至少有多少人存在。
people
:当前已经被“使用”了的人数。
ghost
:当前使用了,且其所在区间还在向右延伸的“幽灵”数。
extend
:已经使用了的非幽灵人中,所在区间依然在向右延伸的人数。
那么检查的大致流程如下:
- 检查是否有关于同一时刻的矛盾称述,再从左往右检查每个时刻。
- 检查“必须存在的区间”左端点为当前时刻的非幽灵。因为幽灵的使用更为自由,所以幽灵更应该保留到万不得已的时候用。
ghost
中那些存在区间是当前考虑的时间前缀的后缀的幽灵,可以通过当前时刻出现的非幽灵的存在区间向前延伸来代替,ghost
减小。 - 若
nows>s_i
,必不合法。接下来就要让nows+extend+ghost=s_i
,若左式小了,就添加一些幽灵(同时被使用的人数增加)。若左式大了,由于ghost
继续往后延伸还可能能被代替,所以先让extend
中的人停止区间延伸,再让ghost
中的停止。 - 检查“必须存在的区间”右端点为当前时刻的非幽灵,他们所在区间可以继续往右延伸,加入
extend
中。 - 所有时刻检查完后,检查
people
是否超过了人数。
代码
#include<bits/stdc++.h>
using namespace std;
#define RI register int
int read() {
int q=0;char ch=' ';
while(ch<'0'||ch>'9') ch=getchar();
while(ch>='0'&&ch<='9') q=q*10+ch-'0',ch=getchar();
return q;
}
const int N=100005;
int T,n,m;
int record_p[N],record_t[N],record_s[N],L[N],R[N],s[N],appear[N],disappear[N];
int check(int lim) {
for(RI i=1;i<=m;++i) s[i]=-1,appear[i]=disappear[i]=0;
for(RI i=1;i<=n;++i) L[i]=m+1,R[i]=0;
for(RI i=1;i<=lim;++i) {
int p=record_p[i],t=record_t[i];
if(s[t]!=-1&&s[t]!=record_s[i]) return 0;
s[t]=record_s[i],L[p]=min(L[p],t),R[p]=max(R[p],t);
}
int nows=0,people=0,ghost=0,extend=0;
for(RI i=1;i<=n;++i) if(R[i]) ++appear[L[i]],++disappear[R[i]];
for(RI i=1;i<=m;++i) {
if(s[i]==-1) continue;
nows+=appear[i];
if(ghost<appear[i]) people+=appear[i]-ghost,ghost=0;
else ghost-=appear[i];
if(nows>s[i]) return 0;
if(nows+ghost+extend<s[i])
people+=s[i]-nows-ghost-extend,ghost+=s[i]-nows-ghost-extend;
if(nows+ghost+extend>s[i]) {
int kl=nows+ghost+extend-s[i];
if(kl<=extend) extend-=kl;
else ghost-=kl-extend,extend=0;
}
nows-=disappear[i],extend+=disappear[i];
}
return people<=n;
}
int main()
{
T=read();
while(T--) {
n=read(),m=read();
for(RI i=1;i<=m;++i)
record_t[i]=read(),record_p[i]=read(),record_s[i]=read()+1;
int l=0,r=m,mid,ans=0;
while(l<=r) {
mid=(l+r)>>1;
if(check(mid)) ans=mid,l=mid+1;
else r=mid-1;
}
printf("%d\n",ans);
}
return 0;
}