题目传送门
题目大意:
给出一个1~n的排列,每个
对应一个
代表这个数的价值
将序列划分成一个前缀和一个后缀,然后移动前后缀中的数到另一边,最终使得前缀中的所有数都小于后缀中的任何一个数,移动任意一个
的花费是
,求最小的花费。
For example, if p=[3,1,2] and a=[7,1,4], then the optimal strategy is: separate p into two parts [3,1] and [2] and then move the 2-element into first set (it costs 4). And if p=[3,5,1,6,2,4], a=[9,1,9,9,1,9], then the optimal strategy is: separate p into two parts [3,5,1] and [6,2,4], and then move the 2-element into first set (it costs 1), and 5-element into second set (it also costs 1).
题目中的解释
思路:
赛中想了个假的三分+DP的方法想莽一波,虽然一开始就觉得三分不靠谱,但是还是试了一下。一直wa到结束。赛后看了dalao的代码,恍然大悟。
枚举以为分界,对于
放到左边,
放到右边,这样原问题就相当于求一个最优的前后缀分割使得移动的花费最小。
仔(kan)细(dalao)思(dai)考(ma)后发现,实际上对于某一种划分而言,在枚举时,对于右边集合而言,他的贡献实际上就是
,而对于左边集合正好相反。
所以我们可以用一棵权值线段树维护答案,每个点表示当前划分下以为分割点时的答案。
初始时我们认为将整个序列划分给右半段,那么对于每个都加上
,因为以比
大的点作为分割点时,
都会被向左边集合移动,产生
的贡献。
然后枚举划分的点,每次将右边集合最左边的移动到左边集合,这时
对于所有大于等于它的
不在有贡献,而是对于所有小于他的产生贡献。在所有贡献中取
就是答案。
代码:
// #pragma GCC optimize(3,"Ofast","inline") #include <cstdio> #include <cmath> #include <cstring> #include <algorithm> #include <queue> #include <map> #include <set> #include <stack> #include <vector> #include <string> #include <iostream> #include <list> #include <cstdlib> #include <bitset> #include <assert.h> #include <time.h> // #define getchar() (p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 1 << 21, stdin), p1 == p2) ? EOF : *p1++) // char buf[(1 << 21) + 1], * p1 = buf, * p2 = buf; //#define int long long #define lowbit(x) (x & (-x)) #define lson root << 1, l, mid #define rson root << 1 | 1, mid + 1, r #define pb push_back typedef unsigned long long ull; typedef long long ll; typedef std::pair<int, int> pii; #define bug puts("BUG") const long long INF = 0x3f3f3f3f3f3f3f3fLL; const int inf = 0x3f3f3f3f; const int mod = 998244353; const double eps = 1e-6; template <class T> inline void read(T &x) { int sign = 1;char c = getchar();x = 0; while (c > '9' || c < '0'){if (c == '-')sign = -1;c = getchar();} while (c >= '0' && c <= '9'){x = x * 10 + c - '0';c = getchar();} x *= sign; } #ifdef LOCAL FILE* _INPUT=freopen("input.txt", "r", stdin); // FILE* _OUTPUT=freopen("output.txt", "w", stdout); #endif using namespace std; const int maxn = 2e5 + 10; ll p[maxn], a[maxn]; ll val[maxn << 2], lazy[maxn << 2]; void pushup(int root) { val[root] = min(val[root << 1], val[root << 1 | 1]); } void pushdown(int root) { if (lazy[root]) { val[root << 1] += lazy[root]; val[root << 1 | 1] += lazy[root]; lazy[root << 1] += lazy[root]; lazy[root << 1 | 1] += lazy[root]; lazy[root] = 0; } } void update(int root, int l, int r, int lt, int rt, int v) { if (l == lt && r == rt) { val[root] += v; lazy[root] += v; return; } int mid = l + r >> 1; pushdown(root); if(rt<=mid) update(lson, lt, rt, v); else if(lt>mid) update(rson, lt, rt, v); else { update(lson, lt, mid, v); update(rson, mid + 1, rt, v); } pushup(root); } int main() { int n; read(n); for (int i = 1; i <= n; ++i) { read(p[i]); } for (int i = 1; i <= n; ++i) { read(a[i]); } for (int i = 1; i <= n; ++i) { update(1, 0, n, p[i], n, a[i]); } ll res = INF << 1; for (int i = 1; i < n; ++i) { update(1, 0, n, p[i], n, -a[i]); update(1, 0, n, 0, p[i] - 1, a[i]); res = min(res, val[1]); } printf("%lld\n", res); }