BZOJ3414: Poi2013 Inspector

来源:互联网 发布:c语言在线编译 输入 编辑:程序博客网 时间:2024/04/19 06:18

题目大意:一天公司有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 707185547using 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();}


 

 

 

0 0
原创粉丝点击