CodeForcse 1305H Kuroni the Private Tutor

CodeForcse 1305H Kuroni the Private Tutor

https://codeforces.com/contest/1305/problem/H

\(m\) 个同学参加了一场考试,有 \(n\) 道题,每道题的分数都是 \(1\) .你碰巧的瞥了一眼最终成绩单,得知了一些信息

  • \(i\) 道题至少有 \(L_i\) 个同学做出来了,至多有 \(R_i\) 个同学做出来
  • \(p_i\) 名的分数为 \(s_i\) , \(i \in [1,q]\)
  • 所有同学的总分为 \(T\)

其中排名的顺序为第一名得分最高,最后一名得分最低,分数相同的顺序任意.

问最多有多少名同学分数和第一名相同,在满足和第一名相同成绩的人数最大化的同时,第一名的分数最大为多少.

特别的,如果不存在满足以上限制的成绩单,输出"-1 -1"

\(1 \le n,m \le 10^5\) , \(0 \le q \le m\)

\(0 \le l_i \le r_i \le m\)

\(1 \le p_i \le m, 0 \le s_i \le n\) ,满足 \(p_i\) 互不相同,且对于任意 \(i,j \in [1,q]\) ,若 \(p_i \le p_j\) ,则 \(s_i \ge s_j\)

Tutorial

考虑假设我们已经决定了每位同学的分数 \(b_1 \le b_2 \le \cdots, \le b_m, \sum b_i = T\) ,想要判断是否存在给每个同学分配它做对的题目集合,使得满足 \(L_i,R_i\) 限制的方案.

发现这个可以建立上下界网络流模型,设立源点和汇点,还有 \(n\) 个点表示题目, \(m\) 个点表示同学.源点向第 \(i\) 个题目的点连接上界为 \(L_i\) 下界为 \(R_i\) 的边,第 \(i\) 个同学向汇点连接上界,下界均为 \(b_i\) 的边.每对题目和同学之间连接上界为 \(1\) ,下界为 \(0\) 的边.

那么我们需要满足两个条件

  • 存在可行流
  • 最大流为 \(T\)

可以利用最小割的模型来思考,可以枚举最小割的形式,得到这样的式子

\[\min\{ sumL_j+B_i+(n-j)(m-i) \} \ge sL \\ \min\{ sumR_j+B_i+(n-j)(m-i) \} \ge T \]

其中 \(sumL_j\) 表示 \(L\) 中前 \(i\) 小的数的和, \(sumR_i\) 表示 \(R\) 中前 \(i\) 小的数的和, \(B_i = \sum_{j=1}^i b_j\) . \(sL\) 表示所有 \(L\) 的和.

这可以用斜率优化做到 \(O(n+m)\) 判断.

显然,和第一名相同的同学数是可以二分的.

而从最后的式子的形式可以看出,若 \(a\) majorize \(b\) ,那么 \(a\) 作为 \(B\) 会比 \(b\) 作为 \(B\) 更优秀,也就是说让排名小的同学分数尽量大是更优的,所以第一名的分数也是可以二分的.

在二分第一名的分数的时候,需要分别考虑一下两种情况

  • \(p_i,s_i,T\) 的限制而判非法

  • \(L_i,R_i\) 的限制而判非法

Code

https://codeforces.com/contest/1305/submission/72366397

#include <algorithm>
#include <cassert>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <vector>
#define debug(...) fprintf(stderr,__VA_ARGS__)
#define invalid0(a,b,c) (A[b]-A[a])*(c-b)>=(A[c]-A[b])*(b-a)
#define invalid1(a,b,c) (ll)(a)*(c-b)<=(A[c]-A[b])
#define fi first
#define se second
using namespace std;
inline char nc()
{
	return getchar();
	static char buf[100000],*l=buf,*r=buf;
	return l==r&&(r=(l=buf)+fread(buf,1,100000,stdin),l==r)?EOF:*l++;
}
template<class T> void read(T &x)
{
	x=0; int f=1,ch=nc();
	while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=nc();}
	while(ch>='0'&&ch<='9'){x=x*10-'0'+ch;ch=nc();}
	x*=f; 
}
template<class T> inline bool Cmin(T &x,T y) {return x>y?x=y,1:0;}
typedef long long ll;
typedef pair<int,int> pii;
const ll INF=1e18;
const int maxn=1e5+50;
int n,m,q; ll T;
int mx;
int re;
int a[maxn];
int b[maxn];
int tmp[maxn];
ll sL;
ll sum;
ll B[maxn]; 
ll L[maxn];
ll R[maxn];
bool check0(ll rest)
{
	vector<pii> rec;
	int now=1;
	for(int i=1;i<=m;++i)
	{
		if(a[i]!=-1)
		{
			if(now<i) rec.push_back(make_pair(now,i-1));
			now=i+1;
		}
	}
	for(int i=0;i<rec.size()&&rest;++i)
	{
		int l=rec[i].fi,r=rec[i].se;
		ll d=(ll)(r-l+1)*(B[r+1]-B[r]);
		if(rest>=d) 
		{
			rest-=d;
			for(int j=l;j<=r;++j) B[j]=B[r+1];
		}
		else
		{
			int x=rest/(r-l+1),y=rest-(ll)x*(r-l+1);
			for(int j=l;j<=r;++j)
			{
				B[j]+=x;
				if(r-j+1<=y) ++B[j];
			}
			rest=0;
		}
	}
	return rest==0;
}
ll cal(ll *A)
{
	static int sta[maxn]; int top=0;
	for(int i=0;i<=n;++i)
	{
		while(top>1&&invalid0(sta[top-1],sta[top],i)) --top;
		sta[++top]=i;
	}
	ll mn=INF;
	for(int i=0;i<=m;++i)
	{
		while(top>1&&invalid1(m-i,sta[top-1],sta[top])) --top;
		int x=sta[top];
		Cmin(mn,A[x]+B[i]+(ll)(n-x)*(m-i));
	}
	return mn;
}
bool check1()
{
//	for(int i=1;i<=n;++i) debug("%lld ",L[i]); debug("\n");
//	for(int i=1;i<=n;++i) debug("%lld ",R[i]); debug("\n");
//	for(int i=1;i<=m;++i) debug("%lld ",B[i]); debug("\n");
	for(int i=1;i<=m;++i) B[i]+=B[i-1];
	assert(B[m]==T);
	return cal(L)>=sL&&cal(R)>=T;
} 
bool sol(int len)
{
	int rec=-1;
	for(int i=m-len+1;i<=m;++i) if(a[i]!=-1)
	{
		if(rec!=-1&&rec!=a[i]) return 0;
		rec=a[i];
	}
	int l,r; bool ok=0;
	if(rec!=-1) l=r=rec;
	else  l=mx,r=min((ll)n,(T-sum)/len+mx);
//	l=r=8;
	while(l<=r)
	{
		int mid=(l+r)>>1;
		for(int i=1;i<=m;++i) B[i]=b[i];
		ll rest=T-sum;
		for(int i=m-len+1;i<=m;++i)
		{
			rest-=mid-B[i]; B[i]=mid;
			tmp[i]=a[i],a[i]=mid;
		}
//		debug("%lld\n",rest);
//		for(int i=1;i<=m;++i) debug("%lld ",B[i]); debug("\n");
		if(rest<0)
		{
			r=mid-1;
		}
		else if(!check0(rest)) 
		{
			l=mid+1;
		}
		else
		{
			if(check1()) ok=1,re=mid,l=mid+1;
			else r=mid-1;
		}
		for(int i=m-len+1;i<=m;++i)
		{
			a[i]=tmp[i];
		}
	}
	return ok;
}
int main()
{
	read(n),read(m);
	for(int i=1;i<=n;++i)
	{
		read(L[i]),read(R[i]);
	}
	read(q);
	memset(a,-1,sizeof(a));
	for(int i=1;i<=q;++i)
	{
		int p,s; read(p),read(s);
		a[p]=s;
	}
	read(T);
	sort(L+1,L+n+1);
	sort(R+1,R+n+1);
	for(int i=1;i<=n;++i)
	{
		L[i]+=L[i-1];
		R[i]+=R[i-1];
	}
	sL=L[n];
	reverse(a+1,a+m+1);
	for(int i=1;i<=m;++i)
	{
		if(a[i]!=-1) mx=a[i];
		b[i]=mx;
		sum+=mx;
	}
	if(sum>T)
	{
		puts("-1 -1");
		return 0;
	} 
	int l=1,r=m,an=-1; re=-1;
	while(l<=r)
	{
		int mid=(l+r)>>1;
		if(sol(mid)) an=mid,l=mid+1;
		else r=mid-1;
	}	
//	cerr<<sol(7)<<endl;
	printf("%d %d\n",an,re);
	return 0;
} 
posted @ 2020-05-28 17:30  LJZ_C  阅读(231)  评论(0编辑  收藏  举报