较简单的一道题,不涉及到任何高级算法,就是码量有点大…
首先,考虑怎样判断没有学生退学的时候是否满足要求
将所有小组的年龄
a
v
e
1
…
m
ave_{1\dots m}
ave1…m 和老师的年龄
a
1
…
n
a_{1\dots n}
a1…n 降序排列后,当且仅当
∀
(
1
≤
i
≤
m
)
\forall (1 \le i \le m)
∀(1≤i≤m) 满足
a
v
e
i
≤
a
i
ave_i \le a_i
avei≤ai 时,才满足题目要求
对于每一个学生退学的情况,只会改变它所在组别 p p p 的平均值,那么只需要判断 a v e p ave_p avep 变化后是否满足条件
现在分两种情况考虑,分别是初始时可以 start lessons
, 和不可以的情况
- 若初始时可以:
那么将改变的元素 p p p 换到相应的位置(保持序列的单点不升),并将 p p p 跨过的区间内的元素同意向 左/右 移动一位,用前缀和判定移动之后是否满足 a v e i ≤ a i ave_i\le a_i avei≤ai 即可 - 若初始不可以:
我们按组别平均值从大到小去匹配老师,每次匹配上剩下老师中年龄最大的,则会剩下若干个组别失配。
如果失配数大于 1 1 1,则输出全零串,因为不管哪个人退学都不能满足要求(还是会有人失配)
否则,还是将 a v e ave ave 改变的组别 p p p 换到相应的位置,并进行与 1 \text {1} 1 相同的操作
时间复杂度 O ( n log n ) \mathcal O (n \log n) O(nlogn)
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<vector>
using namespace std;
const int Maxn=1e5+10;
const double eps=1e-9;
struct node{
double ave;
vector <int> bin;
}g[Maxn];
int a[Maxn],b[Maxn];
int s[Maxn];
vector <int> c[Maxn];
bool flag[Maxn];
int n,m,tot;
inline bool cmp(node x,node y)
{
return x.ave<y.ave;
}
inline int read()
{
int s=0,w=1;
char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();}
while(ch>='0' && ch<='9')s=(s<<3)+(s<<1)+(ch^48),ch=getchar();
return s*w;
}
void work2(int pos)
{
s[0]=1;
for(int i=1;i<pos;++i)
s[i]=(n-m+i>0 && g[i].ave<=1.0*b[n-m+i]+eps && s[i-1]);
s[pos]=s[pos-1];
for(int i=pos+1;i<=m;++i)
s[i]=(n-m+i-1 && g[i].ave<=1.0*b[n-m+i-1]+eps && s[i-1]);
for(int i=pos;i<=m;++i)
for(int j=c[i].size()-1;j>=0;--j)
{
int val=c[i].size();
double cur=g[i].ave*(1.0*val);
cur-=a[c[i][j]],cur/=1.0*(val-1);
if(cur>=g[pos].ave)continue;
int l=0,r=pos-1;
l+=n-m,r+=n-m;
while(l<r)
{
int mid=(l+r)>>1;
if(cur<=1.0*b[mid]+eps)r=mid;
else l=mid+1;
}
l+=m-n;
flag[c[i][j]]=s[l];
}
}
void work1()
{
s[0]=0;
for(int i=1;i<=m;++i)
s[i]=(n-m+i-1 && g[i].ave<=1.0*b[n-m+i-1]+eps);
for(int i=1;i<=m;++i)
s[i]+=s[i-1];
for(int i=1;i<=m;++i)
for(int j=0;j<c[i].size();++j)
{
int val=c[i].size();
if(1.0*a[c[i][j]]+eps>=g[i].ave)
{flag[c[i][j]]=1;continue;}
double cur=g[i].ave*(1.0*val);
cur-=a[c[i][j]],cur/=1.0*(val-1);
if(cur>b[n]+eps)continue;
int l=i,r=m;
r+=n-m,l+=n-m;
while(l<r)
{
int mid=(l+r)>>1;
if(cur<=1.0*b[mid]+eps)r=mid;
else l=mid+1;
}
l+=m-n;
flag[c[i][j]]=(s[l]-s[i]==l-i);
}
}
int main()
{
// freopen("in.txt","r",stdin);
// freopen("out.txt","w",stdout);
int T=read();
int COUNT=0;
while(T--)
{
++COUNT;
n=read(),m=read();
for(int i=1;i<=n;++i)
b[i]=read();
for(int i=1;i<=m;++i)
{
int tmp=read();
g[i].bin.clear();
c[i].clear();
g[i].ave=0.0;
for(int j=1;j<=tmp;++j)
{
int x=read();
g[i].ave+=x,a[++tot]=x;
g[i].bin.push_back(tot);
}
g[i].ave/=(1.0*tmp);
}
sort(g+1,g+1+m,cmp);
sort(b+1,b+1+n);
for(int i=1;i<=m;++i)
for(int j=0;j<g[i].bin.size();++j)
c[i].push_back(g[i].bin[j]);
int pos=0;
for(int i=m;i;--i)
{
if(g[i].ave<=1.0*b[n-m+i])continue;
if(pos)
{
for(int i=1;i<=tot;++i)
putchar('0');
putchar('\n');
goto GG;
}
else pos=i;
}
if(pos)work2(pos);
else work1();
for(int i=1;i<=tot;++i)
putchar(flag[i]+'0');
putchar('\n');
GG:
fill(flag,flag+1+tot,0);
fill(s,s+1+m,0);
fill(a+1,a+1+tot,0);
fill(b+1,b+1+n,0);
tot=0;
}
return 0;
}