Codeforces Round #318(Div. 1) 573 D. Bear and Cavalry【dp+矩阵+线段树优化】
Would you want to fight against bears riding horses? Me neither.
Limak is a grizzly bear. He is general of the dreadful army of Bearland. The most important part of an army is cavalry of course.
Cavalry of Bearland consists of n warriors and n horses. i-th warrior has strength wi and i-th horse has strength hi. Warrior together with his horse is called a unit. Strength of a unit is equal to multiplied strengths of warrior and horse. Total strength of cavalry is equal to sum of strengths of all n units. Good assignment of warriors and horses makes cavalry truly powerful.
Initially, i-th warrior has i-th horse. You are given q queries. In each query two warriors swap their horses with each other.
General Limak must be ready for every possible situation. What if warriors weren't allowed to ride their own horses? After each query find the maximum possible strength of cavalry if we consider assignments of all warriors to all horses that no warrior is assigned to his own horse (it can be proven that for n ≥ 2 there is always at least one correct assignment).
Note that we can't leave a warrior without a horse.
The first line contains two space-separated integers, n and q (2 ≤ n ≤ 30 000, 1 ≤ q ≤ 10 000).
The second line contains n space-separated integers, w1, w2, ..., wn (1 ≤ wi ≤ 106) — strengths of warriors.
The third line contains n space-separated integers, h1, h2, ..., hn (1 ≤ hi ≤ 106) — strengths of horses.
Next q lines describe queries. i-th of them contains two space-separated integers ai and bi (1 ≤ ai, bi ≤ n, ai ≠ bi), indices of warriors who swap their horses with each other.
Print q lines with answers to queries. In i-th line print the maximum possible strength of cavalry after first i queries.
4 2 1 10 100 1000 3 7 2 5 2 4 2 4
5732 7532
3 3 7 11 5 3 2 1 1 2 1 3 2 3
44 48 52
7 4 1 2 4 8 16 32 64 87 40 77 29 50 11 18 1 5 2 7 6 2 5 6
9315 9308 9315 9315
Clarification for the first sample:
Horses: 3 7 2 5
After first query situation looks like the following:
Horses: 3 5 2 7
We can get 1·2 + 10·3 + 100·7 + 1000·5 = 5732 (note that no hussar takes his own horse in this assignment).
After second query we get back to initial situation and optimal assignment is 1·2 + 10·3 + 100·5 + 1000·7 = 7532.
Clarification for the second sample. After first query:
Horses: 2 3 1
Optimal assignment is 7·1 + 11·2 + 5·3 = 44.
Then after second query 7·3 + 11·2 + 5·1 = 48.
Finally 7·2 + 11·3 + 5·1 = 52.
题意:给出n个战士和他们各自的n批马,我们可以把马换给别的战士,但是都得换(即战士不能拥有原来的马),我们要每个战士乘以给他换的那个马的和最大。q次询问,每次把2个战士的马交换,输出和最大的答案。
思路:首先如果没有限制,那么根据排序不等式,肯定按顺序匹配战士和马最好。但是现在有了战士不能和自己的马匹配的限制。
于是就有了一个重要的性质:最优匹配的前提下,排序后第i号战士只会与[i-2,i+2]号马匹配
(证明:
设有一种情况i对应i+3
i—–(i+3)
i+1—(i+2)
i+2—(i)
i+3—(i+1)
那么,对于i,i+1来说,必定在(i–i+2),(i+1–i+3)中有一个限制必选,否则交换i,i+1更优
同理,(i–i)(i+2–i+3),(i–i+1)(i+3–i+3)中有一个限制比选
发现与限制是一组排列不符,而且i对应i+2时只有(i-i+2)(i+1-i)(i+2-i+1)这种可能
)
于是就可以DP了,
设
那么
这个转移画画图就可以得到。
这样就可以
#include <iostream> #include <cstdio> #include <cstring> #include <algorithm> #include <cmath> #include <queue> #include <string> #include <map> #include <cstring> #define INF 0x3f3f3f3f #define ms(x,y) memset(x,y,sizeof(x)) using namespace std; typedef long long ll; typedef unsigned long long ull; typedef pair<int, int> P; const int maxn = 30010; const int mod = 998244353; #define lson l,m,rt<<1 #define rson m+1,r,rt<<1|1 struct node { ll v; int id; }; bool cmp(node a, node b) { return a.v < b.v; } int posa[maxn], posb[maxn], ban[maxn]; ll w1[maxn], w2[maxn], w3[maxn], f[maxn]; node a[maxn], b[maxn]; void cal(int i) { w1[i] = w2[i] = w3[i] = -INF; ll t1 = -INF, t2 = -INF; if (i >= 1 && ban[i] != i) w1[i] = a[i].v*b[i].v; if (i >= 2 && ban[i] != i - 1 && ban[i - 1] != i) w2[i] = a[i].v*b[i - 1].v + a[i - 1].v*b[i].v; if (i >= 3 && ban[i] != i - 2 && ban[i - 1] != i&&ban[i - 2] != i - 1) t1 = a[i].v*b[i - 2].v + a[i - 1].v*b[i].v + a[i - 2].v*b[i - 1].v; if (i >= 3 && ban[i] != i - 1 && ban[i - 1] != i - 2 && ban[i - 2] != i) t2 = a[i].v*b[i - 1].v + a[i - 1].v*b[i - 2].v + a[i - 2].v*b[i].v; w3[i] = max(t1, t2); } int main() { int n, q; cin >> n >> q; for (int i = 1; i <= n; i++) { cin >> a[i].v; a[i].id = i; } for (int i = 1; i <= n; i++) { cin >> b[i].v; b[i].id = i; } sort(a + 1, a + n + 1, cmp); sort(b + 1, b + n + 1, cmp); for (int i = 1; i <= n; i++) { posa[a[i].id] = i; posb[b[i].id] = i; } for (int i = 1; i <= n; i++) { ban[i] = posb[a[i].id]; } for (int i = 1; i <= n; i++) { cal(i); } for (int k = 0; k < q; k++) { int x, y; cin >> x >> y; x = posa[x], y = posa[y]; swap(ban[x], ban[y]); for (int i = max(1, x - 5); i <= min(n, x + 5); i++) cal(i); for (int i = max(1, y - 5); i <= min(n, y + 5); i++) cal(i); f[0] = 0; for (int i = 1; i <= n; i++) { f[i] = f[i - 1] + w1[i]; if (i >= 2) f[i] = max(f[i], f[i - 2] + w2[i]); if (i >= 3) f[i] = max(f[i], f[i - 3] + w3[i]); } cout << f[n] << endl; } return 0; }
但是有更高效的做法
令
那么
这个转移可以被描述成矩阵
因为矩乘满足结合律,所以可以用线段树维护
(未完待续)