测试
$ \text{Solution} $
首先拆开最初那个小式子:
然后看看两行合并起来的式子:
操作:决定是否交换一些位置上的 \(x_i\),\(y_i\).
注意到无论怎么操作,前面这半个式子的值都是一定的,那么我们只需要使后面这个式子最小就可以了。
由于每个位置交换或不交换可以看作 \(1\) 和 \(0\),且每个操作对结果造成的影响比较小,容易想到动态规划这些小的影响从而得到最优解。注意到式子里有个前缀和,且这个前缀和的值域很小, \(10^4\) 级别。所以考虑设一维跟前缀和有关的 \(\operatorname{dp}\).
开始 \(\operatorname{dp}\)!
-
发现前缀和不太好从之前状态确定,所以采用刷表法。
-
注意到 $ {\forall k \in [1,n], \sum_{i=1}^k x_i+y_i } $ 为定值,所以如果确定了操作完前 \(k\) 个位置后的 \(\sum_{i=1}^kx_i\),我们就可以 \(O(1)\) 得到此时的 \(\sum_{i=1}^ky_i\)。
-
设 \(\operatorname{dp_{k,S}}\) 表示当前考虑到第 \(k\) 个位置,操作后 \(s = \sum_{i=1}^kx_i\) 时 \(\sum_{i=1}^kx_i\sum_{j=1}^{i-1}x_j + \sum_{i=1}^ky_i\sum_{j=1}^{i-1}y_j\) 的值。
-
对于每个位置都有两个方程,分别对应交换或者不交换的情况。
-
边界条件 \(\operatorname{dp}_{0,0}=0\),其他的位置初始都为正无穷。
可以写出以下方程:
若在这一位不交换,
若在这一位交换,直接把上面式子的 \(x_k\) 和 \(y_k\) 换过来即可,
最后,枚举前缀和 \(s\),答案就是最小的 \(\operatorname{dp}_{n,s}\) 乘以 \(2\) 加上一开始左半边的式子。
关于复杂度:
-
注意到 \(\sum_{i=1}^{k-1}(x_i+y_i)\) 可以在扫描过程中顺便 \(O(1)\) 处理出来,所以不用算进复杂度里。
-
注意到一开始左半边那个式子可以对于每组数据 \(O(n)\) 预处理出来。
-
设 \(v\) 为前缀和值域,则最后的时间复杂度为 \(O(\sum n \times \sum v)\)
实现细节:
- 注意两行前缀和爆出 \(10000\) 的情况,因为是两行,所以最大的前缀和可能会是 \(20000\) 这个量级的,但是爆的情况肯定不优,可以特判掉也可以直接数组开大一点。
#include <bits/stdc++.h>
using namespace std;
#define lint long long
#include <"Jairon.h">
#define maxn 1000010
#define SIZE 5010
int n,ans=0;
int a[maxn];
int b[maxn];
int pre[maxn];
int dp[110][20010];
int main(){
int T=read(_);
while(T--){
ans=0; read(n);
FOR(i,1,n){ read(a[i]); }
FOR(i,1,n){ read(b[i]); }
if(n==1){ puts("0"); continue; }
FOR(i,1,n){ ans+=sqr(a[i])+sqr(b[i]); }
FOR(i,1,n){ pre[i]=pre[i-1]+a[i]+b[i]; }
ans*=(n-1); int Mn=inf,res=0;
FOR(i,1,n){ memset(dp[i],0x3f,sizeof dp[i]); }
dp[0][0]=0;
FOR(i,1,n){
FOR(j,0,pre[i-1]){
chmin(dp[i][j+a[i]],dp[i-1][j]+(j)*a[i]+(pre[i-1]-j)*b[i]);
chmin(dp[i][j+b[i]],dp[i-1][j]+(j)*b[i]+(pre[i-1]-j)*a[i]);
}
}
FOR(j,0,pre[n]){ if(dp[n][j]==inf){ continue; } chmin(Mn,ans+2*dp[n][j]); }
outn(Mn);
}
return 0;
}
/**/

测试
浙公网安备 33010602011771号