P3553 [POI2013]INS-Inspector
Captain_Paul
2018-11-06 21:42:02
题意:有$n$个人和$m$条记录,每个人只会在一个连续的时间段工作,每条记录$t,k,s$表示在$t$时刻,编号为$k$的人说除他以外还有$s$个人,求一个最大的$A$,使前$A$条记录不产生矛盾
------------
显然要二分答案,转化为判定性问题
定义几个变量:
$L_i,R_i$表示第$i$个人一定在工作的时间段
$st_i,ed_i$表示$i$时间点对应区间开始和结束的人数
$cnt_i$表示$i$时间点剩余的人数
------------
通过观察可以发现答案不可行的判断条件
- 如果两条信息中,时间相同但剩余人数不同,那就不可行
- 如果在一个人的工作区间中,有一条其他人的信息说除了他自己以外没人了,那也不可行
------------
除去这两种情况,就一定可以构造出一种方案了
但是构造出的人数可能超过$n$个
所以需要计算符合条件的最小人数
再定义几个变量:
$now$表示当前必需的人数
$tot$表示当前符合条件的最小总人数
$done$表示已经过了对应区间但还继续工作的人数
$todo$表示还没到对应区间但提前开始工作的人数
每到一个时刻$i$,比较一下$now+done+todo$和$cnt_i$的大小关系
- 如果$now+done+todo<cnt_i$,那么说明当前人数不足,需要让一些人提前工作。所以增加$tot$,并修改$todo$
- 如果$now+done+todo>cnt_i$,那么说明人数多了,需要让一些人结束工作。那么就先让已经过了对应区间的人结束工作,如果还多,就再让提前工作的人结束工作。
这里产生了一个问题:$todo$表示的人对应区间还没到为什么可以结束工作呢?
因为不一定每一个人都有其对应的区间,这些人可以随时开始,随时停止,结束工作的$todo$对应的是这一部分人
这真的是一道神题$Orz$
```
#include<cstdio>
#include<cstring>
#include<cctype>
#include<algorithm>
#define reg register
using namespace std;
const int N=1e5+5;
struct node
{
int x,tim,lef;
}q[N];
int n,m,L[N],R[N],st[N],ed[N],cnt[N],tot,now,done,todo;
inline int read()
{
int x=0,w=1;
char c=getchar();
while (!isdigit(c)&&c!='-') c=getchar();
if (c=='-') c=getchar(),w=-1;
while (isdigit(c))
{
x=(x<<1)+(x<<3)+c-'0';
c=getchar();
}
return x*w;
}
inline bool check(int k)
{
tot=now=done=todo=0;
for (reg int i=1;i<=n;i++) L[i]=N,R[i]=0;
for (reg int i=1;i<=m;i++) st[i]=ed[i]=cnt[i]=0;
for (reg int i=1;i<=k;i++)
{
L[q[i].x]=min(L[q[i].x],q[i].tim);
R[q[i].x]=max(R[q[i].x],q[i].tim);
if (cnt[q[i].tim]&&cnt[q[i].tim]!=q[i].lef) return 0;
cnt[q[i].tim]=q[i].lef;
}
for (reg int i=1;i<=n;i++) if (R[i]) ++st[L[i]],++ed[R[i]];
for (reg int i=1;i<=m;i++)
{
if (!cnt[i]) continue;
now+=st[i]; if (now>cnt[i]) return 0;
if (st[i]<=todo) todo-=st[i]; else tot+=st[i]-todo,todo=0;
if (now+done+todo<cnt[i]) tot+=cnt[i]-now-done-todo,todo=cnt[i]-now-done;
else if (now+todo>cnt[i]) todo=cnt[i]-now,done=0;
else done=cnt[i]-now-todo;
now-=ed[i]; done+=ed[i];
}
return tot<=n;
}
inline void work()
{
n=read(),m=read();
for (reg int i=1;i<=m;i++) q[i].tim=read(),q[i].x=read(),q[i].lef=read()+1;
int l=2,r=m,ans=1;
while (l<=r)
{
int mid=(l+r)>>1;
if (check(mid)) ans=mid,l=mid+1;
else r=mid-1;
}
printf("%d\n",ans);
}
int main()
{
for (reg int T=read();T;T--) work();
return 0;
}
```