洛谷P2672 推销员
沙雕贪心......
我一开始想的是倒着来,每次减去一个。
然后我们就有两个决策:去掉最后一个/去掉前面某一个。
然后第一个决策用并查集维护,第二个决策用线段树即可。仔细想想觉得普及组不会考这种东西,慌得一批。
然后又发现可能有问题:你可能取x个的时候不从x + 1转移过来,而是x + 2
然后就不会了。
然后看提解发现正解是顺着来......什么沙雕。
结论:若取x个的时候最优解是集合S,那么取x+1个时的最优解集合一定包含S。(说明了上面我的做法是对的)
证:
即证对于每一个取x+1的方案p,若不包含S,都可以找到一个包含S的方案比它更优。
设取x个的最优方案为r
考虑最右那一个:
①p的最后那个等于r的最右那一个时,前面我们随便去掉一个不与r配对的位置d,然后p一定还与r不同。
我们把p调整成r,然后加上d,这样就比原来的p更优了。
②p的最后那个小于r的最后那一个时,我们同样去掉一个d,然后调整,最后加上d,就会更优。
③p的最后那个大于r的最后那个时,把p的最后那个去掉,同时p的价值减去(2 * 从r最后到p最后的距离)。
这样就相当于情况①中去掉d之后的p了。
然后调整成r之后把原来p的最后加上,再加上减去的价值,就会比原来的p更优。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include <cstdio> 2 #include <algorithm> 3 #include <queue> 4 5 const int N = 100010; 6 7 int a[N], x[N], g[N]; 8 std::priority_queue<int> Q; 9 10 int main() { 11 int n; 12 scanf("%d", &n); 13 for(int i = 1; i <= n; i++) { 14 scanf("%d", &x[i]); 15 x[i] <<= 1; 16 } 17 for(int i = 1; i <= n; i++) { 18 scanf("%d", &a[i]); 19 } 20 21 for(int i = n; i >= 1; i--) { 22 if(x[i] + a[i] > x[g[i + 1]] + a[g[i + 1]]) { 23 g[i] = i; 24 } 25 else { 26 g[i] = g[i + 1]; 27 } 28 } 29 30 int now = g[1]; 31 32 int ans = a[now] + x[now], pos = 1; 33 printf("%d\n", ans); 34 35 for(int i = 2; i <= n; i++) { 36 while(pos < now) { 37 Q.push(a[pos]); 38 pos++; 39 } 40 if(pos == now) { 41 pos++; 42 } 43 if(!Q.empty() && Q.top() > a[g[now + 1]] + x[g[now + 1]] - x[now]) { 44 ans += Q.top(); 45 Q.pop(); 46 } 47 else { 48 ans -= x[now]; 49 now = g[now + 1]; 50 ans += a[now] + x[now]; 51 } 52 printf("%d\n", ans); 53 } 54 55 return 0; 56 }
然后我又打了一开始那个线段树的想法......
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include <cstdio> 2 #include <algorithm> 3 #include <queue> 4 5 const int N = 100010, INF = 0x3f3f3f3f; 6 7 int a[N], x[N], ans[N]; 8 int small[N << 2], fa[N]; 9 10 int find(int x) { 11 if(fa[x] == x) { 12 return x; 13 } 14 return fa[x] = find(fa[x]); 15 } 16 17 inline void pushup(int o) { 18 int ls = o << 1; 19 int rs = ls | 1; 20 if(a[small[ls]] <= a[small[rs]]) { 21 small[o] = small[ls]; 22 } 23 else { 24 small[o] = small[rs]; 25 } 26 return; 27 } 28 29 void build(int l, int r, int o) { 30 if(l == r) { 31 small[o] = r; 32 return; 33 } 34 int mid = (l + r) >> 1; 35 build(l, mid, o << 1); 36 build(mid + 1, r, o << 1 | 1); 37 pushup(o); 38 return; 39 } 40 41 int ask(int L, int R, int l, int r, int o) { 42 if(L <= l && r <= R) { 43 return small[o]; 44 } 45 int mid = (l + r) >> 1; 46 47 if(R <= mid) { 48 return ask(L, R, l, mid, o << 1); 49 } 50 if(mid < L) { 51 return ask(L, R, mid + 1, r, o << 1 | 1); 52 } 53 54 int as = ask(L, R, l, mid, o << 1); 55 int t = ask(L, R, mid + 1, r, o << 1 | 1); 56 if(a[t] < a[as]) { 57 as = t; 58 } 59 return as; 60 } 61 62 void change(int p, int l, int r, int o) { 63 if(l == r) { 64 a[r] = INF; 65 return; 66 } 67 int mid = (l + r) >> 1; 68 if(p <= mid) { 69 change(p, l, mid, o << 1); 70 } 71 else { 72 change(p, mid + 1, r, o << 1 | 1); 73 } 74 pushup(o); 75 return; 76 } 77 78 int main() { 79 int n, sum = 0; 80 scanf("%d", &n); 81 for(int i = 1; i <= n; i++) { 82 scanf("%d", &x[i]); 83 x[i] <<= 1; 84 } 85 for(int i = 1; i <= n; i++) { 86 scanf("%d", &a[i]); 87 fa[i] = i; 88 sum += a[i]; 89 } 90 sum += x[n]; 91 ans[n] = sum; 92 93 build(1, n, 1); 94 95 int now = n; 96 for(int i = n - 1; i >= 1; i--) { 97 int pos = ask(1, now - 1, 1, n, 1); 98 if(a[now] + x[now] - x[find(now - 1)] > a[pos]) { 99 sum -= a[pos]; 100 change(pos, 1, n, 1); 101 fa[pos] = find(pos - 1); 102 } 103 else { 104 sum -= (a[now] + x[now] - x[find(now - 1)]); 105 now = find(now - 1); 106 } 107 ans[i] = sum; 108 } 109 110 for(int i = 1; i <= n; i++) { 111 printf("%d\n", ans[i]); 112 } 113 return 0; 114 }
话说这个代码我调都没调,一次就写对了。