CF626G Raffles

刚开始ZZ了加了个dcmp调精度结果把自己的精度搞爆了,后来改了下EPS就过了

假设现在没有修改,我们设某个奖池里的放入的彩票数量为\(s_i\),很显然\((\frac{s_i}{s_i+l_i})'=-\frac{l_i}{(s_i+l_i)^2}<0\),因此随着\(s_i\)的增加它的增量是递减的

因此我们容易发现,每次选择放入一张彩票增量最大的奖池放彩票是最优的,因此可以先处理出答案

考虑修改,首先我们要注意一些特殊情况:

  • 增加操作时,若手头的彩票数还有空余,那么可以多放一张
  • 减少操作时,若\(s_x=l_i\),那么可以退还出一张彩票到手头

然后考虑我们修改后可能会引起将某个奖池的彩票拿出放到另一个奖池可以增大答案的情况

于是我们再维护一下拿走一张彩票减量最小的奖池,将这个值和放入一张彩票增量最大的值进行比较即可

考虑这样做的复杂度,套用一下势能分析不难发现复杂度修改的次数是\(O(q)\)级别的

因此总复杂度\(O((n+q)\log n)\),实现起来有些小细节,懒得写可删除堆了就直接用set

#include<cstdio>
#include<set>
#include<cmath>
#define RI register int
#define CI const int&
using namespace std;
const int N=200005;
const double EPS=1e-10;
inline int dcmp(const double& x)
{
	return fabs(x)<EPS?0:(x<0?-1:1);
}
struct element
{
	double val; int pos;
	inline element(const double& Val=0,CI Pos=0) { val=Val; pos=Pos; }
	friend inline bool operator < (const element& A,const element& B)
	{
		return dcmp(A.val-B.val)!=0?dcmp(A.val-B.val)<0:A.pos<B.pos;
	}
}; int n,t,q,l[N],s[N],x,y,p[N]; double ans; set <element> A,B;
typedef set <element>:: iterator SI; 
#define calc(x,y) (1.0*p[x]*(y)/((y)+l[x]))
inline void reset(void)
{
	while (t)
	{
		if (A.empty()) break; SI it=--A.end(); ans+=it->val;
		int id=it->pos; A.erase(it); B.erase(element(calc(id,s[id])-calc(id,s[id]-1),id));
		if (++s[id],s[id]<l[id]) A.insert(element(calc(id,s[id]+1)-calc(id,s[id]),id));
		B.insert(element(calc(id,s[id])-calc(id,s[id]-1),id)); --t;
	}
	while (!A.empty()&&!B.empty())
	{
		SI mx=--A.end(),mi=B.begin(); if (mx->val<=mi->val) break;
		ans+=mx->val-mi->val; int imx=mx->pos,imi=mi->pos; A.erase(mx); B.erase(mi);
		A.erase(element(calc(imi,s[imi]+1)-calc(imi,s[imi]),imi));
		B.erase(element(calc(imx,s[imx])-calc(imx,s[imx]-1),imx));
		if (++s[imx],s[imx]<l[imx]) A.insert(element(calc(imx,s[imx]+1)-calc(imx,s[imx]),imx));
		B.insert(element(calc(imx,s[imx])-calc(imx,s[imx]-1),imx));
		if (--s[imi]) B.insert(element(calc(imi,s[imi])-calc(imi,s[imi]-1),imi));
		A.insert(element(calc(imi,s[imi]+1)-calc(imi,s[imi]),imi));
	}
}
inline void Add(CI x)
{
	ans-=calc(x,s[x]); A.erase(element(calc(x,s[x]+1)-calc(x,s[x]),x));
	B.erase(element(calc(x,s[x])-calc(x,s[x]-1),x));
	if (++l[x],ans+=calc(x,s[x]),t) ++s[x],--t,ans+=calc(x,s[x])-calc(x,s[x]-1);
	if (s[x]<l[x]) A.insert(element(calc(x,s[x]+1)-calc(x,s[x]),x));
	if (s[x]) B.insert(element(calc(x,s[x])-calc(x,s[x]-1),x)); reset();
}
inline void Del(CI x)
{
	ans-=calc(x,s[x]); A.erase(element(calc(x,s[x]+1)-calc(x,s[x]),x));
	B.erase(element(calc(x,s[x])-calc(x,s[x]-1),x));
	if (s[x]==l[x]) --s[x],++t; --l[x]; ans+=calc(x,s[x]);
	if (s[x]<l[x]) A.insert(element(calc(x,s[x]+1)-calc(x,s[x]),x));
	if (s[x]) B.insert(element(calc(x,s[x])-calc(x,s[x]-1),x)); reset();
}
int main()
{
	RI i; for (scanf("%d%d%d",&n,&t,&q),i=1;i<=n;++i) scanf("%d",&p[i]);
	for (i=1;i<=n;++i) scanf("%d",&l[i]),A.insert(element(calc(i,1)-calc(i,0),i));
	for (reset(),i=1;i<=n;++i) if (s[i]) B.insert(element(calc(i,s[i])-calc(i,s[i]-1),i));
	for (i=1;i<=q;printf("%.10lf\n",ans),++i) if (scanf("%d%d",&x,&y),x==1) Add(y); else Del(y);
	return 0;
}
posted @ 2020-12-03 11:30  空気力学の詩  阅读(117)  评论(0编辑  收藏  举报