C. Schedule Management
链接: problem
分析:
这句话应该会让你立刻联想到二分。很明显,如果你能以在 t t t 时间之前完成任务的方式分配工人,那么你也能在 t + 1 t+1 t+1 或更长时间之前完成所有任务。
如何检查任务是否能在某个时间 t t t 前完成呢?这意味着所有工人都有 t t t 个小时来完成某些任务。如果所有任务都需要 2 2 2 小时才能完成,那么他们每个人都可以完成 ⌊ t 2 ⌋ \lfloor \frac t 2 \rfloor ⌊2t⌋ 项任务。因此,他们总共可以完成 ⌊ t 2 ⌋ ⋅ n \lfloor \frac t 2 \rfloor \cdot n ⌊2t⌋⋅n 项任务。
如何将 1 1 1 小时的任务纳入其中呢?我们可以重新分配任务,让每个工人先完成自己擅长的任务,如果时间充裕,再完成其他任务。
总体思路如下。让每个工人 i i i 完成 m i n ( t , c n t i ) min(t, \mathit{cnt}_i) min(t,cnti) 小时的任务。 1 1 1 小时的任务,其中 c n t i \mathit{cnt}_i cnti 是 i i i 个工人所精通的任务数。然后记住他们能完成多少 2 2 2 /小时的任务,即 ⌊ t − m i n ( t , c n t i ) 2 ⌋ \lfloor \frac{t - min(t, \mathit{cnt}_i)}{2} \rfloor ⌊2t−min(t,cnti)⌋ 。最后,记住他们精通的任务中有多少没有时间完成,即 c n t i − m i n ( t , c n t i ) \mathit{cnt}_i - min(t, \mathit{cnt}_i) cnti−min(t,cnti) 。如果未完成任务数的总和不超过他们有时间完成的任务数的总和,那么所有任务都可以在 t t t 时间内完成。
最糟糕的情况是,如果将所有任务分配给一名工人,而他又不精通其中任何一项,那么完成所有任务可能需要 2 m 2m 2m 个小时。
总体复杂度:每个测试案例 O ( n log m ) O(n \log m) O(nlogm) 。
代码:
#include<bits/stdc++.h>
using namespace std;
const int N=2e5+10,M=1e9+10;
typedef long long ll;
typedef pair<int,int> PII;
int T;
int cnt[N];
int n,m;
bool check(int t)
{
ll t1=0,t2=0;
for(int i=1;i<=n;i++)
{
if(t>=cnt[i]) t1+=(t-cnt[i])/2;
else t2+=(cnt[i]-t);
}
if(t1>=t2) return true;
else return false;
}
void solve()
{
cin>>n>>m;
for(int i=1;i<=n;i++) cnt[i]=0;
for(int i=1,x;i<=m;i++) cin>>x,cnt[x]++;
int l=0,r=m+1;
while(l+1!=r)
{
int mid=(l+r)/2;
if(check(mid)) r=mid;
else l=mid;
}
cout<<r<<endl;
return ;
}
int main()
{
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
cin>>T;
while(T--)
solve();
return 0;
}