CF1621E New School

Statement

较简单的一道题,不涉及到任何高级算法,就是码量有点大…

首先,考虑怎样判断没有学生退学的时候是否满足要求
将所有小组的年龄 a v e 1 … m ave_{1\dots m} ave1m 和老师的年龄 a 1 … n a_{1\dots n} a1n 降序排列后,当且仅当 ∀ ( 1 ≤ i ≤ m ) \forall (1 \le i \le m) (1im) 满足 a v e i ≤ a i ave_i \le a_i aveiai 时,才满足题目要求

对于每一个学生退学的情况,只会改变它所在组别 p p p 的平均值,那么只需要判断 a v e p ave_p avep 变化后是否满足条件

现在分两种情况考虑,分别是初始时可以 start lessons, 和不可以的情况

  1. 若初始时可以:
    那么将改变的元素 p p p 换到相应的位置(保持序列的单点不升),并将 p p p 跨过的区间内的元素同意向 左/右 移动一位,用前缀和判定移动之后是否满足 a v e i ≤ a i ave_i\le a_i aveiai 即可
  2. 若初始不可以:
    我们按组别平均值从大到小去匹配老师,每次匹配上剩下老师中年龄最大的,则会剩下若干个组别失配。
    如果失配数大于 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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值