题解:
我会说我想这道普及组题都想了好久么。。。。
不熟练的普及组选手.jpg
最后随便猜了一个结论居然是对的。。。
放结论:
假设x = i的最优决策为f[i], 那么f[i + 1]一定包括f[i].
也就是说f[i+1]一定可以由f[i]推出来。
所以f[i]一定是由f[i+1]中选定的所有点中去掉一个得来的。
即下一次选择一定是基于上一次选择的。。。
至于证明嘛。。。感性理解吧。
因为选哪户人家推销其实相互没太多联系。而且选i个推销肯定选满是最划得来的。。。。
大概是这么理解的吧。(反向理解可能更加好理解一点)
所以有了这个结论就很好解决了。
因为f[n]的最优决策肯定是n个点全选。
所以我们从这个已知的往前推。
于是我们来观察这n个点,到底删掉哪个减掉的疲劳值最小呢?
首先选中的这些点可以看做一个有序点集,位置由s[i]的大小所决定。
因此我们记last[x]为在x前面的第一个点的id, Next[x]为在x后面的第一个点的id。
于是有两种情况:
1,删去的点在末尾。
此时减掉的疲劳值为a[x] + 2 * (s[x] - s[last[x]])
2,删去的点在中间。
那么此时对走路的距离没有任何影响,因此我们只需要减a[x]即可。
值得注意的是,当末尾点k为a[i]最小的点时, 我们需要再取出次小的点来做比较,因为a[k]虽然最小,但由于要走很多路,所以a[k] + 2 * (s[k] - s[last[k]])并不一定小于a[i](i < k, 且i ∈ 点集),因此我们需要进一步比较再做取舍。
而k不是a[i]最小的点时,因为a[k]就已经大于a[i]了,所以减去的疲劳值肯定要大于a[i]了,因此直接删最小的点就可以了。
这里的最小和次小由于要支持插入,删除,查询,因此我们使用优先队列来实现。
为了方便查询元素的id,,,于是我又建了一个结构体、。
代码:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include<bits/stdc++.h> 2 using namespace std; 3 #define R register int 4 #define LL long long 5 #define getchar() *o++ 6 #define AC 100100 7 char READ[5001000], *o = READ; 8 int n; 9 int s[AC], a[AC], ans[AC], Next[AC], last[AC], r; 10 struct node{ 11 int w, id; 12 }; 13 14 struct cmp{ 15 bool operator () (node a, node b) 16 { 17 return a.w > b.w; 18 } 19 }; 20 21 priority_queue <node, vector<node>, cmp> q; 22 23 inline int read() 24 { 25 int x = 0; char c = getchar(); 26 while(c > '9' || c < '0') c = getchar(); 27 while(c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar(); 28 return x; 29 } 30 31 void pre() 32 { 33 n = read(); 34 for(R i = 1; i <= n; i++) s[i] = read(); 35 for(R i = 1; i <= n; i++) 36 { 37 a[i] = read(); 38 q.push((node){a[i], i}); 39 ans[n] += a[i]; 40 last[i] = i - 1; 41 if(i != n) Next[i] = i + 1; 42 } 43 ans[n] += s[n] * 2, r = n; 44 } 45 46 void work() 47 { 48 int b = n - 1;node x, now; 49 for(R i = b; i; i--) 50 { 51 x = q.top(); 52 if(x.id == r) 53 { 54 q.pop(); 55 now = q.top(); 56 if(now.w < x.w + (s[x.id] - s[last[x.id]]) * 2) 57 { 58 q.pop(); 59 Next[last[now.id]] = Next[now.id]; 60 last[Next[now.id]] = last[now.id]; 61 ans[i] = ans[i + 1] - now.w; 62 q.push(x); 63 } 64 else//不然就删后者 65 { 66 Next[now.id] = 0; 67 ans[i] = ans[i + 1] - x.w - 2 * (s[x.id] - s[last[x.id]]); 68 r = last[x.id];//移动末项 69 } 70 } 71 else//不然直接就删中间的了 72 { 73 q.pop(); 74 Next[last[x.id]] = Next[x.id]; 75 last[Next[x.id]] = last[x.id]; 76 ans[i] = ans[i + 1] - x.w; 77 } 78 } 79 for(R i = 1; i <= n; i++) printf("%d ", ans[i]); 80 } 81 82 int main() 83 { 84 freopen("in.in", "r", stdin); 85 fread(READ, 5000000, 1, stdin); 86 pre(); 87 work(); 88 fclose(stdin); 89 return 0; 90 }