题意:有一个卖面包的姑娘,如果过了w分钟仍旧没有客人来的话,他就会睡着,如果客人在他睡着的时候来的话,客人会把她叫醒,然后离开。
每个客人会以不同的价格买面包,姑娘会买给第i个客人(i-1)%3+1块面包,求在收到的钱做多的情况下的最小的w.
做法:唉,没想到....只能看大神的。10W个人用线段树是没错的。要求最小的w,可是为了可以让客人买到面包,这里的w的值是离散的,即相邻两个客人之间的间隔。一开始还脑残的以为可以2分答案...
先对每个客人根据到来的时间进行排序,然后算出每个客人的w,然后再根据w排序(只要客人的w相同,枚举到这个时间间隔是,这个客人就一定可以买到东西)。
建立线段树是,节点记录的是现在的区间中的客人数,还有现在可以在各种购买量下获得的利润。
还有一点时,如果a客人买了x片面包,a+1就可以买(x+1)%3+1片,这个在两区间合并时注意,见pushup
(a+b)%mod=(a%mod+b%mod)%mod 唉,傻了...
#include<cstdio>
#include<cstring>
#include<algorithm>
#define LL long long
using namespace std;
const int LMT=100003;
const int WEI=3;
#define left l,m,x<<1
#define right m+1,r,x<<1|1
double p[LMT];
int sum[LMT<<2];
LL get[WEI+1][LMT<<2];
double ansg,anst;
/*************
节点记录了当区间最左侧的购买序列为I使整个区间可以获得的
钱,取余数记得这种分割法啊。。。
******/
struct person
{
int t,p;
bool operator<(const person &y)const
{
return t<y.t;
}
}per[LMT];
struct ma
{
int id,w;
bool operator<(const ma &y)const
{
return w<y.w;
}
}man[LMT];
void init(void)
{
ansg=anst=0;
memset(sum,0,sizeof(sum));
memset(get,0,sizeof(get));
}
void pushup(int x)
{
sum[x]=sum[x<<1]+sum[x<<1|1];
int tem,i;
for(i=1;i<=WEI;i++)
{
tem=(i+sum[x<<1]-1)%WEI+1;
get[i][x]=get[i][x<<1]+get[tem][x<<1|1];
}
}
void update(int pos,LL p,int l,int r,int x)
{
if(l==r)
{
sum[x]++;
for(int i=1;i<=WEI;i++)
get[i][x]=i*p;
return;
}
int m=(l+r)>>1;
if(pos<=m)update(pos,p,left);
if(pos>m)update(pos,p,right);
pushup(x);
}
int main(void)
{
int T,n,i,j;
double tem;
scanf("%d",&T);
while(T--)
{
init();
scanf("%d",&n);
for(i=0;i<n;i++)scanf("%d",&per[i].p);
for(i=0;i<n;i++)scanf("%d",&per[i].t);
sort(per,per+n);
man[0].w=per[0].t;
man[0].id=0;
for(i=1;i<n;i++)
{
man[i].w=per[i].t-per[i-1].t;
man[i].id=i;
}
sort(man,man+n);
for(i=0;i<n;)
{
j=i;
while(j<n&&man[i].w==man[j].w)
{
update(man[j].id,per[man[j].id].p,0,n-1,1);
j++;
}
tem=get[1][1]*1.0/sum[1];
if(ansg<tem)
{
ansg=tem;
anst=man[i].w;
}
i=j;
}
printf("%.6lf %.6lf\n",anst,ansg);
}
return 0;
}