题目描述:
D. Bear and Cavalry
time limit per test:3 seconds
memory limit per test:256 megabytes
input:standard input
output:standard output
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.
Input
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.
Output
Print q lines with answers to queries. In i-th line print the maximum possible strength of cavalry after first i queries.
题解:
好题。首先是一个贪心性质:我们从大到小排序,如果没有限制,那么肯定是按照顺序来配对是结果最大的。但是现在有一些限制的关系。那么怎么办呢?我们探索一下。我们设dp【i】,表示i是最后一个,解决完dp【i】的值。那么推i:如果wi可以和hi匹配,那么我们直接让wi和hi匹配就是最好的。如果wi和hi不可以,那么我们让wi-1和hi以及wi和hi-1来匹配,这样一定是最好的。但是有一个问题,可能wi-2和hi-2不能匹配,所以可能i会涉及到i-2. 但是有一个重要的性质,我们发现i最多只会涉及到i-2,再往前就一定可以通过调整来不那么远。 那么我们最裸的dp就是:dp【i】,然后o(3)的暴力转移。
但是这道题目可以用线段树来加速。毕竟每一次询问只改了很少的值。主要想明白两端区间怎么合并,就是怎么pushUp。怎么才能合并呢?dp的向前延伸和向后延伸都是2.l mid mid+1 r 这两个怎么合并?看mid+1最多会受到前面多少的影响.mid,mid-1. 那么暴力枚举mid+1和哪3(也可能是2或者自己)个发生关系。所以我们只需要把l到mid的后面的2个元素可能性的去掉多维护一些东西就行了。 我们维护一个3*3的矩阵。 0、1、2分别代表去掉前、后0.1.2个元素之后的dp值。这样在合并的时候只需要大约3*3*3的暴力合并就行了。
有一个特殊的地方就是:当区间长度小于4的时候会比较麻烦,因此我们区间小于8之后就不再分,而是改用暴力。
重点:
(1)贪心的想乘
(2)有条件限制的时候不用直接想,可以转成dp简化思维
(3)发现性质,i只会最多和i-2发生关系
(4)每次只修改一点,并且dp的后效性很小,可以用线段树加速
代码:
#pragma comment(linker, "/STACK:102400000,102400000")
#include <iostream>
#include <cstdio>
#include <cstring>
#include <string>
#include <cmath>
#include <ctype.h>
#include <limits.h>
#include <cstdlib>
#include <algorithm>
#include <vector>
#include <queue>
#include <map>
#include <stack>
#include <set>
#include <bitset>
#define CLR(a) memset(a, 0, sizeof(a))
#define REP(i, a, b) for(ll i = a;i < b;i++)
#define REP_D(i, a, b) for(ll i = a;i <= b;i++)
typedef long long ll;
using namespace std;
const ll maxn = 30000+100;
const ll MAX_NODE = maxn*4 + 10;
ll n, q;
struct info
{
ll no, s, wno;
info(ll _no = 0, ll _s = 0, ll _wno = 0)
{
no = _no;
s = _s;
wno = _wno;
}
};
info h[maxn], w[maxn];
ll posH[maxn], posW[maxn];
ll f[maxn];
ll tree[MAX_NODE][3][3];
ll dp(ll l, ll r)
{
f[l-1] = 0;
for(ll i = l; i<=r; i++)
f[i] = -1;
for(ll i = l; i<=r; i++)
{
ll j = i;
if(j>=l&&f[j-1]!=-1&&w[i].no!=h[j].no)
{
f[i] =max(f[i], f[j-1]+(ll)w[i].s*(ll)h[j].s);
}
j--;
if(j>=l&&f[j-1]!=-1)
{
if(w[i].no!=h[j].no&&w[j].no!=h[i].no)
f[i] = max(f[i], f[j-1]+(ll)w[i].s*(ll)h[j].s+(ll)w[j].s*(ll)h[i].s);
}
j--;
if(j>=l&&f[j-1]!=-1)
{
if(w[j].no!=h[j+1].no&&w[j+1].no!=h[j+2].no&&w[i].no!=h[j].no)
f[i] = max(f[i], f[j-1]+(ll)w[j].s*(ll)h[j+1].s+(ll)w[j+1].s*(ll)h[j+2].s+(ll)w[i].s*(ll)h[j].s);
if(w[j].no!=h[i].no&&w[j+1].no!=h[j].no&&w[i].no!=h[j+1].no)
f[i] = max(f[i], f[j-1]+(ll)w[j].s*(ll)h[i].s+(ll)w[j+1].s*(ll)h[j].s+(ll)w[i].s*(ll)h[j+1].s);
}
}
return f[r];
}
void pushUp(ll rt, ll l, ll r)//下面的都已经好了.且一定有下面的
{
ll mid = ((l + r)>>1), lRt = (rt<<1), rRt = ((rt<<1)|1);
if(mid-l+1<4||r-mid<4)
{
for(ll i = 0; i<=2; i++)
{
for(ll j = 0; j<=2; j++)
{
ll tmp = dp(l+i, r-j);
tree[rt][i][j] = tmp;
}
}
}
else
{
for(ll i = 0; i<=2; i++)
for(ll j = 0; j<=2; j++)
tree[rt][i][j]=-1;
for(ll xa = 0; xa<=2; xa++)
{
for(ll ya = 0; ya<=2; ya++)
{
if(tree[lRt][xa][ya]!=-1)
{
for(ll xb=0; xb+ya<=3&&xb<=2; xb++)
{
for(ll yb = 0; yb<=2; yb++)
{
if(tree[rRt][xb][yb]!=-1)
{
ll &key = tree[rt][xa][yb];
ll a = tree[lRt][xa][ya], b = tree[rRt][xb][yb];
if(ya==0&&xb==0)
key = max(key, a+b);
if(ya==0||xb==0)
continue;
if(ya==1&&xb==1)
{
if(w[mid].no!=h[mid+1].no&&w[mid+1].no!=h[mid].no)
key = max(key, a+b+(ll)w[mid].s*(ll)h[mid+1].s+(ll)w[mid+1].s*(ll)h[mid].s);
}
if((ya==1&&xb==2)||(ya==2&&xb==1))
{
ll i = mid - ya+1;
ll j = i+1;
ll k = j+1;
if(w[i].no!=h[j].no&&w[j].no!=w[k].no&&w[k].no!=h[i].no)
key = max(key, a+b+(ll)w[i].s*(ll)h[j].s+(ll)w[j].s*(ll)h[k].s+(ll)w[k].s*(ll)h[i].s);
if(w[i].no!=w[k].no&&w[j].no!=h[i].no&&w[k].no!=h[j].no)
key = max(key, a+b+(ll)w[i].s*(ll)h[k].s+(ll)w[j].s*(ll)h[i].s+(ll)w[k].s*(ll)h[j].s);
}
}
}
}
}
}
}
}
}
void dfs(ll rt, ll l, ll r)
{
if(r-l+1 < 4)
return;
ll mid = ((l + r)>>1), lRt = (rt<<1), rRt = ((rt<<1)|1);
dfs(lRt, l, mid);
dfs(rRt, mid+1, r);
pushUp(rt, l, r);
}
void initail()
{
dfs(1, 1, n);
}
void change(ll a, ll b, ll c, ll d, ll rt, ll l, ll r)
{
if(r - l + 1< 4)
return;
ll mid = ((l + r)>>1), lRt = (rt<<1), rRt = ((rt<<1)|1);
if((a>=l&&a<=mid)||(b>=l&&b<=mid)||(c>=l&&c<=mid)||(d>=l&&d<=mid))
{
change(a, b, c, d, lRt, l, mid);
}
if((a>=mid+1&&a<=r)||(b>=mid+1&&b<=r)||(c>=mid+1&&c<=r)||(d>=mid+1&&d<=r))
{
change(a, b, c, d, rRt, mid + 1, r);
}
pushUp(rt, l, r);//向上push
}
void solve()
{
initail();
while(q--)
{
ll a, b;
scanf("%I64d%I64d", &a, &b);
swap(w[posW[a]].no, w[posW[b]].no);
if(n>=4)
{
change(posW[a], posW[b], posH[w[posW[a]].no], posH[w[posW[b]].no], 1, 1, n);
printf("%I64d\n", tree[1][0][0]);
else
{
printf("%I64d\n", dp(1, n));
}
}
}
bool cmp(info a, info b)
{
return a.s < b.s;
}
int main()
{
freopen("4Din.txt", "r", stdin);
//freopen("4Dout.txt", "w", stdout);
while(scanf("%I64d%I64d", &n, &q)!=EOF)
{
for(ll i = 1; i<=n; i++)
{
scanf("%I64d", &w[i].s);
w[i].no = i;
w[i].wno = i;
}
for(ll i = 1; i<=n; i++)
{
scanf("%I64d", &h[i].s);
h[i].no = i;
}
sort(w+1, w+1+n, cmp);
sort(h+1, h+1+n, cmp);
for(ll i = 1; i<=n; i++)
{
posW[w[i].wno] = i;
posH[h[i].no] = i;
}
solve();
}
return 0;
}