题目链接:https://codeforces.com/contest/573/problem/D
有 \(n\) 个人和 \(n\) 匹马,第 \(i\) 个人对应第 \(i\) 匹马。第 \(i\) 个人能力值 \(w_i\),第 \(i\) 匹马能力值 \(h_i\),第 \(i\) 个人骑第 \(j\) 匹马的总能力值为 \(w_i\times h_j\),整个军队的总能力值为 \(\sum w_i\times h_j\)(一个人只能骑一匹马,一匹马只能被一个人骑)。有一个要求:每个人都不能骑自己对应的马。让你制定骑马方案,使得整个军队的总能力值最大。现在有 \(m\) 个操作,每次给出 \(a,b\),交换 \(a\) 和 \(b\) 对应的马。每次操作后你都需要输出最大的总能力值。
\(n\leq 30000,m\leq 10000\)。
考虑一个最简单的问题:给定两个长度相同正整数序列 \(a,b\),要求将 \(b\) 序列重新排序,最大化 \(\sum a_i\times b_i\)。
这个问题显然只需要将 \(a,b\) 分别排序然后一一匹配即可。因为如果 \(a<b<c<d\),\(ad+bc<ac+bd\)。
那么再考虑本题不带修改的情况。多了每一个人不能匹配与他编号相同的马的条件。依然将两个序列排序,我们可以证明对于任意一个人 \(i\),能和它匹配的马的编号一定在 \([i-2,i+2]\) 内。注意这里是排序后的序列。
因为假设 \(a_i\) 匹配了 \(b_{i+3}\),那么在 \(a_{i+1},a_{i+2},a_{i+3}\) 中至少可以找到一个元素,交换它和 \(a_i\) 所匹配马后依然没有人匹配和它编号相同的马。画一下就很好理解了。
所以我们设 \(f_i\) 表示前 \(i\) 个人恰好匹配完前 \(i\) 匹马的最大权值,那么 \(f_i\) 只可能从 \(f_{i-3},f_{i-2},f_{i-1}\) 转移而来。
其中 \(x_i,y_i,z_i\) 就是前 \(1,2,3\) 对分别匹配的最大贡献,可以暴力枚举所有情况。
观察到每次修改只是单点修,并且询问是全局询问(因为我们排了序后原来编号就是乱序了),而且这个转移是 \(\max\) 里面套个加法,所以直接上动态 dp 即可。
时间复杂度 \(O((n+m)\omega^3\log n)\),其中 \(\omega=3\)。
代码#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=30010;
const ll Inf=1e18;
int n,m,id[N];
struct node
{
int a,id;
}a[N],b[N];
bool cmp(node x,node y)
{
return x.a<y.a;
}
struct Matrix
{
ll a[4][4];
Matrix() { memset(a,0xcf,sizeof(a)); }
friend Matrix operator *(Matrix a,Matrix b)
{
Matrix c;
for (int i=1;i<=3;i++)
for (int j=1;j<=3;j++)
for (int k=1;k<=3;k++)
c.a[i][j]=max(c.a[i][j],a.a[i][k]+b.a[k][j]);
return c;
}
}mat[N],fad;
struct SegTree
{
Matrix a[N*4];
void update(int x,int l,int r,int k)
{
if (l==r) { a[x]=mat[l]; return; }
int mid=(l+r)>>1;
if (k<=mid) update(x*2,l,mid,k);
else update(x*2+1,mid+1,r,k);
a[x]=a[x*2]*a[x*2+1];
}
}seg;
ll calc(int l,int r)
{
if (l<=0) return -Inf;
int n=r-l+1,c[5]={0,0,0,0,0}; ll ans=-Inf;
for (int i=1;i<=n;i++) c[i]=l+i-1;
do {
ll sum=0;
for (int i=1;i<=n;i++)
{
if (a[l+i-1].id==b[c[i]].id) { sum=-Inf; break; }
sum+=1LL*a[l+i-1].a*b[c[i]].a;
}
ans=max(ans,sum);
} while (next_permutation(c+1,c+1+n));
return ans;
}
void upd(int i)
{
mat[i].a[1][1]=-Inf; mat[i].a[1][2]=-Inf; mat[i].a[1][3]=calc(i-2,i);
mat[i].a[2][1]=0; mat[i].a[2][2]=-Inf; mat[i].a[2][3]=calc(i-1,i);
mat[i].a[3][1]=-Inf; mat[i].a[3][2]=0; mat[i].a[3][3]=calc(i,i);
seg.update(1,1,n,i);
}
int main()
{
scanf("%d%d",&n,&m);
for (int i=1;i<=n;i++)
scanf("%d",&a[i].a),a[i].id=i;
for (int i=1;i<=n;i++)
scanf("%d",&b[i].a),b[i].id=i;
sort(a+1,a+1+n,cmp); sort(b+1,b+1+n,cmp);
for (int i=1;i<=n;i++) id[b[i].id]=i;
for (int i=1;i<=n;i++) upd(i);
fad.a[1][3]=0;
while (m--)
{
int x,y;
scanf("%d%d",&x,&y);
swap(b[id[x]].id,b[id[y]].id);
swap(id[x],id[y]);
for (int i=0;i<=2;i++)
{
if (id[x]+i<=n) upd(id[x]+i);
if (id[y]+i<=n) upd(id[y]+i);
}
printf("%lld\n",(fad*seg.a[1]).a[1][3]);
}
return 0;
}