zoj zju 3606 Lazy Salesgirl
题意:有一个售货员,n个顾客,卖给 i 这个顾客的价格是pi,i 顾客来买东西的时刻为 ti
如果有w的时间段没人来买东西,售货员就会睡着,下一个顾客来时会叫醒她,但是不买东西
售货员会卖给第i个来(相对顺序)的顾客 1 + ((i - 1) mod 3)的数量的面包 即 1 2 3 中的一个
问使得平均的销售额最大的最小w以及此时的平均销售额是多少( 总的销售额/顾客的个数)
做法:我觉得关键是要抓住题目的特点,深挖下去,售货员每隔w时间会睡着,那就意味着,
1:如果有个顾客和上一个顾客间的时间间隔超过了w,这个顾客就不糊买东西,而与上一个顾客来的时间间隔小于等于w的顾客肯定能买到面包
所以我们只需枚举每个时间间隔既可,答案肯定就是某个时间间隔,一旦时间间隔定了,那能卖的面包数以及买到东西的顾客数也就定了
2:买到面包的数量问题,由题目给出的式子可得,卖出面包数量的序列 为1 2 3 1 2 3.。。。所以如果知道了某个人买了几个面包,那么可以
推算出接下来第x个人买了几个面包,因为最多也只有三种情况,所以把三种情况的结果都保存一下,就用到了三个线段树,其实就是线段树的域开个二维数组
线段树的域:
sum[rt]:记录当前节点的区间内共有几个顾客
pp[rt][3]:记录当区间最左边的人分别买了 1 2 3个面包时总的销售额
那么我们可以通过线段树将这个结果传递上去
关键代码:
for(int i=0;i<3;i++)
pp[x][i]=pp[x<<1][i]+pp[x<<1|1][(sum[x<<1]+i)%3];
知道了左子树第一个人买的面包个数,和左子树的人数,自然就可以推出右子树第一个人买的面包个数,三种情况都记录一下,然后再传递上去
最后的结果是pp[1][0],因为第一个人肯定只买了一个面包;
#include<cstdio> #include<cstring> #include<algorithm> using namespace std; #define lson l,m,x<<1 #define rson m+1,r,x<<1|1 const int maxn = 100010; int sum[maxn<<2]; double pp[maxn<<2][3]; struct node{ int id,len,num,tt; bool operator < (const node &cmp) const{ return tt<cmp.tt; } }a[maxn],cnt[maxn]; void update(int pos,int l,int r,int x){ sum[x]++; if(l==r){ for(int i=0;i<3;i++) pp[x][i]=1.0*(i+1)*a[pos].num; return ; } int m=(l+r)>>1; if(pos<=m) update(pos,lson); else update(pos,rson); for(int i=0;i<3;i++) pp[x][i]=pp[x<<1][i]+pp[x<<1|1][(sum[x<<1]+i)%3]; } int main(){ int t,n,i,j; scanf("%d",&t); while(t--){ scanf("%d",&n); for(i=1;i<=n;i++) scanf("%d",&a[i].num); for(i=1;i<=n;i++) scanf("%d",&a[i].tt); sort(a+1,a+n+1); a[0].tt=0; for(i=1;i<=n;i++) { cnt[i].tt=a[i].tt-a[i-1].tt; cnt[i].id=i; } memset(sum,0,sizeof(sum)); memset(pp,0,sizeof(pp)); sort(cnt+1,cnt+n+1); double aver_ans=0,w_ans; for(i=1,j=1;i<=n;i=j){ while(cnt[j].tt==cnt[i].tt && j<=n) //j<=n update(cnt[j].id,1,n,1); j++; } double tmp=pp[1][0]*1.0/sum[1]; if(tmp>aver_ans){ aver_ans=tmp; w_ans=cnt[i].tt; } } printf("%.6lf %.6lf\n",w_ans,aver_ans); } return 0; }