题意:有一个卖面包的姑娘,如果过了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; }