武装捍卫 dp

· · 题解

事先声明,本题解为诡异做法,运用了大量 dp 以及数据结构优化。

题解

首先连通这个条件不好刻画,考虑他的冲要条件,手磨一下能发现即为

\nexists\space i\in[1,n),s.t.\space \max\limits_{x=1}^i p_x=i

然后不管贪心了,直接对着这个东西设 dp_{i,j} 表示考虑前 i 个数,在相对排名下满足上面式子的最小位置是 j 的最大价值。

考虑加入 i 时的转移,第一种是 i 对前面 j 个没有影响,即对于前面产生的逆序对数 \leq i-j-1

dp_{i,j}=dp_{i-1,j} (a_i\leq 0)

第二种是插入后的相对排名 \leq j,那么此时只有 i 符合上面式子条件,那么对于前面产生的逆序对数 \geq i-j。

dp_{i,i}=dp_{i-1,j}+a_i(i-j)(a_i\leq0)

注意到第二维变化时一定变成 i 所以删掉第二维,令 dp_i 表示前 i 个连通且前面不存在满足上面条件的最大价值。

令 s_i=\sum\limits_{x=1}^i a_x,si_i=\sum\limits_{x=1}^i a_xx,则有

dp_i=dp_j+si_{i-1}-si_j-(s_{i-1}-s_j)(j+1)+a_i(i-1)(a_i>0) 最后的构造是简单的,可以根据最优决策跳回去,中间的数对前面产生的逆序对个数已知。 好的,现在你学会暴力数据结构通过这道构造题了,赶快去应用吧。 ### code ::::success[$\mathcal{O}(n\log V)$] ```cpp #include <bits/stdc++.h> #define int long long const int M = 1e14; using namespace std; int t, n, a[200005], f[200005], g[200005], s[200005], si[200005], cnt, h[200005], p[200005]; struct line{ __int128 k, b; } l[200005]; struct linetree{ int bel[200005], ls[200005], rs[200005], point, rt; __int128 calc(int c, __int128 x){ return l[c].k * x + l[c].b; } bool cmp(int x, int a, int b){ return calc(a, x) > calc(b, x); } void update(int l, int r, int &rt, int c){ if(!rt) rt = ++point; if(!bel[rt]){ bel[rt] = c; return; } int mid = l + r >> 1; if(cmp(mid, c, bel[rt])) swap(bel[rt], c); if(l == r) return; if(cmp(l, c, bel[rt])) update(l, mid, ls[rt], c); if(cmp(r, c, bel[rt])) update(mid + 1, r, rs[rt], c); } pair<int, __int128> query(int l, int r, int rt, int x){ if(!rt) return {0, 0}; pair<int, __int128> ans = {bel[rt], calc(bel[rt], x)}; int mid = l + r >> 1; if(mid >= x){ pair<int, __int128> val = query(l, mid, ls[rt], x); if(!ans.first || val.second > ans.second) ans = val; } else{ pair<int, __int128> val = query(mid + 1, r, rs[rt], x); if(!ans.first || val.second > ans.second) ans = val; } return ans; } } li; struct segtree{ int s[800005]; void built(int l, int r, int rt){ s[rt] = r - l + 1; if(l == r) return; int mid = l + r >> 1; built(l, mid, rt << 1); built(mid + 1, r, rt << 1 | 1); } int query(int l, int r, int rt, int c){ s[rt]--; if(l == r) return l; int mid = l + r >> 1; if(s[rt << 1 | 1] > c) return query(mid + 1, r, rt << 1 | 1, c); return query(l, mid, rt << 1, c - s[rt << 1 | 1]); } } tr; template<typename type> inline void read(type &x){ x=0;bool flag(0);char ch=getchar(); while(!isdigit(ch)) flag=ch=='-',ch=getchar(); while(isdigit(ch)) x=(x<<3)+(x<<1)+(ch^48),ch=getchar(); flag?x=-x:0; } template<typename type> inline void write(type x){ x<0?x=-x,putchar('-'):0;static short Stack[50],top(0); do Stack[++top]=x%10,x/=10; while(x); while(top) putchar(Stack[top--]|48); putchar(' '); } signed main(){ read(t); while(t--){ read(n); for( int i = 1; i <= n; i++ ) read(a[i]); for( int i = 1; i <= n; i++ ){ s[i] = s[i - 1], si[i] = si[i - 1]; if(a[i] > 0) s[i] += a[i], si[i] += a[i] * i; } f[1] = 0; for( int i = 2; i <= n; i++ ){ l[i - 1] = {-(i - 1), f[i - 1] - si[i - 1] + i * s[i - 1]}; li.update(-M, M, li.rt, i - 1); if(a[i] > 0){ pair<int, __int128> val = li.query(-M, M, li.rt, s[i - 1]); g[i] = val.first; f[i] = val.second + si[i - 1] - s[i - 1] + a[i] * (i - 1); } else{ pair<int, __int128> val = li.query(-M, M, li.rt, s[i - 1] + a[i]); g[i] = val.first; f[i] = val.second + si[i - 1] - s[i - 1] + a[i] * i; } } int pos = n; while(pos > 1){ if(a[pos] > 0) h[pos] = pos - 1; else h[pos] = pos - g[pos]; for( int i = pos - 1; i > g[pos]; i-- ){ if(a[i] > 0) h[i] = i - g[pos] - 1; else h[i] = 0; } pos = g[pos]; } tr.built(1, n, 1); for( int i = n; i >= 1; i-- ) p[i] = tr.query(1, n, 1, h[i]); write(f[n]), putchar('\n'); for( int i = 1; i <= n; i++ ) write(p[i]); putchar('\n'); memset(li.bel, 0, sizeof(int) * (n + 2)); memset(li.rs, 0, sizeof(int) * (n + 2)); memset(li.ls, 0, sizeof(int) * (n + 2)); li.rt = li.point = 0; } return 0; } ``` ::::