题面
阿明是一名推销员,他奉命到螺丝街推销他们公司的产品。螺丝街是一条死胡同,出口与入口是同一个,街道的一侧是围墙,另一侧是住户。螺丝街一共有N家住户,第ii家住户到入口的距离为Si米。由于同一栋房子里可以有多家住户,所以可能有多家住户与入口的距离相等。阿明会从入口进入,依次向螺丝街的X家住户推销产品,然后再原路走出去。 阿明每走1米就会积累1点疲劳值,向第ii家住户推销产品会积累Ai 点疲劳值。阿明是工作狂,他想知道,对于不同的X,在不走多余的路的前提下,他最多可以积累多少点疲劳值。
分析
当时是搜线段树的题,想随便做一做,然后就选了一道提高-的涨涨信心
结果做着做着思路越来越偏,这是当时记事本上写的内心os(d是距离,c是推销花费,dmax最大,dcmax次大):
我想先试着倒着做,最大的情况肯定是唯一的sum=dmax* 2+ segma c
然后来减,试着减少推销一个住户,那减少的住户一定要是贡献最少的
贡献少-->距离短or推销花费少
如果距离短(短于最远距离),本来也就不会有任何的贡献,所以只需要找出推销花费最少的一个??
sum-=c[i]即可
那如果要修改最远距离呢?那sum=sum-dmax*2+dcmax*2-c[i],dmax=dcmax,好像就可以了?
而且这个d好像可以
所以要比较一下这两个哪个更小就ok了?
然后就开始用两个优先队列搞了,基本上算个模拟题了,好吧,一次A了,确实挺涨信心,可是我想做线段树啊!!!
后来根据第二组样例发现,如果说最远距离和推销花费最少的是同一户人家,特判一下,即拿最远距离和花费第二少比较。
然后看了一发线段树的题解,怎么这么复杂...算了算了另外找题
代码
#include<bits/stdc++.h> using namespace std; #define N 100100 #define ll long long ll n,sum; ll ans[N],vis[N]; struct email1 { ll ad,ac,aid; }a[N]; struct email2 { ll bd,bc,bid; }b[N]; bool operator <(email1 x,email1 y) { return x.ad<y.ad; } bool operator <(email2 x,email2 y) { return x.bc>y.bc; } priority_queue<email1>qd; priority_queue<email2>qc; int main() { scanf("%d",&n); for(ll i=1;i<=n;i++) { ll d; scanf("%lld",&d); a[i].ad=b[i].bd=d; a[i].aid=b[i].bid=i; } for(ll i=1;i<=n;i++) { ll c; scanf("%lld",&c); sum+=c; a[i].ac=b[i].bc=c; qc.push(b[i]);qd.push(a[i]); } email1 pos=qd.top(); sum+=pos.ad*2;ans[n]=sum; for(ll i=1;i<=n-1;i++) { email1 d,d2; while(vis[qd.top().aid])qd.pop();d=qd.top();qd.pop(); while(vis[qd.top().aid])qd.pop();d2=qd.top(); email2 c,c2; while(vis[qc.top().bid])qc.pop();c=qc.top();qc.pop(); while(vis[qc.top().bid])qc.pop();c2=qc.top(); if(d.aid==c.bid) { if(d.ad*2+d.ac-d2.ad*2<c2.bc) { sum=sum-(d.ad*2+d.ac-d2.ad*2); vis[d.aid]=1; } else { sum=sum-c2.bc; vis[c2.bid]=1; qd.push(d);qc.push(c); } } else { if(d.ad*2+d.ac-d2.ad*2<c.bc) { sum=sum-(d.ad*2+d.ac-d2.ad*2); vis[d.aid]=1; qc.push(c); } else { sum=sum-c.bc; vis[c.bid]=1; qd.push(d); } } ans[n-i]=sum; } for(ll i=1;i<=n;i++) printf("%lld ",ans[i]); return 0; }