题目大意:一天公司有n个员工和m个员工记录,每个员工只会在连续的一段时间内工作。现在给出m条记录分别是谁写的、什么时候写的以及写的时候除了他还有多少人。求最大的k使得前k条记录互不矛盾
挺神的题...
首先二分答案转成判定性问题,这样记录就没有先后顺序之分了
然后考虑什么情况会出现矛盾:
1.记录本身的矛盾:两条记录是同一时刻,但人数不同
判断方法:这个是很显然的了,可以直接特判
2.记录的记载人产生的约束关系而形成的矛盾:
比如说一个人在a时刻写过记录,又在b时刻写过记录,这就说明a到b的时刻他都在公司,这时假如中间有个人写除了他以外没人了,那肯定就不对了
判断方法:我们可以对每个人记录一下最晚开始时间和最早结束时间,当成一条线段,对于每个时间节点,看他被几条线段覆盖,如果线段条数大于当前时间记录的人数,就说明一定无解
3.根据记录可以构造出一种方案,但是会超过n个人的限制:
这个跟上一条的不同点在于,他是可以根据约束条件构造出方案的
比如1时刻1记录有两个人,2时刻1记录有1个人,3时刻1记录有两个人,显然n=3时是可以的,但n=2就不行
所以我们只需要算出他的最小符合条件的人数,判断一下是否小于等于n就可以了,那么怎么算呢?
首先我们按照时间顺序来扫,还是像2一样每个人对应一段区间
除了当前必须人数(2中的线段条数)now和当前最小符合条件的人数total以外,我们开两个变量来辅助计算
done表示所对应区间已经过去了,还留下来可以继续工作的人数
notbegin表示所对应区间还没到,但是提前开始工作的人数
每到一个新的时刻,我们可以比较一下now+done+notbegin与tot[i](这个点记录的人数)的大小
如果比tot[i]小,说明人数还不够,则我们需要再让一些人提前开始工作
如果比tot[i]大,那说明人数多了,我们就优先让对应区间已经过去了的人结束工作,如果还是多,那就让notbegin的人结束工作
这时可能会有一个问题,notbegin的区间还没到怎么可以结束工作呢,这样不是相当于无解了吗?
我一开始也是这么想的,于是直接return false交了一发,WA了
原因就是,并不是所有的人都有一个确定的对应区间,有很多人都是没写记录的,那么这些人就可以随时开始随时结束,这里notbegin减下去的是这些人
具体可以看看代码
#include<iostream>
#include<cstdio>
#include<algorithm>
#define N 100010
#define xrf 707185547
using namespace std;
int n,m;
int t[N],u[N],v[N];
int mn[N],mx[N];
int tot[N];
int st[N],en[N];
bool check(int M)
{
int i,j;
for(i=1;i<=n;i++)
{
mn[i]=xrf;
mx[i]=-xrf;
}
for(i=1;i<=m;i++)
tot[i]=st[i]=en[i]=0;
for(i=1;i<=M;i++)
{
mn[u[i]]=min(mn[u[i]],t[i]);
mx[u[i]]=max(mx[u[i]],t[i]);
if(tot[t[i]]&&tot[t[i]]!=v[i]+1) return false;
tot[t[i]]=v[i]+1;
}
for(i=1;i<=n;i++)
if(mx[i]!=-xrf)
{
st[mn[i]]++;
en[mx[i]]++;
}
int total=0,now=0;
int done=0,notbegin=0;
for(i=1;i<=m;i++)
if(tot[i])
{
now+=st[i];
if(now>tot[i]) return false;
if(st[i]<=notbegin) notbegin-=st[i];
else total+=st[i]-notbegin,notbegin=0;
if(now+notbegin+done<tot[i]) total+=tot[i]-now-notbegin-done,notbegin=tot[i]-now-done;
else
{
if(now+notbegin>tot[i]) /*return false;*/notbegin=tot[i]-now,done=0;
else done=tot[i]-now-notbegin;
}
now-=en[i];done+=en[i];
}
if(total>n) return false;
return true;
}
void doit()
{
scanf("%d%d",&n,&m);
int i,j;
for(i=1;i<=m;i++)
scanf("%d%d%d",&t[i],&u[i],&v[i]);
int l=1,r=m+1,mid;
while(l<r)
{
mid=(l+r)>>1;
if(check(mid)) l=mid+1;
else r=mid;
}
printf("%d\n",l-1);
}
int main()
{
int T;
scanf("%d",&T);
while(T--)
doit();
}