信息学进阶之路
二项式定理
(A + B) * (A + B) = A * A + 2 * A * B + B * B
编程学习曲线与进程
题目
第4题 货物搬运A
在A国,快递路线被抽象成了一条直线。我们在这里选择一个区间[1, n],表示有n个城市,每个城市以1~ n中的一个数作为编号,按编号从小到大坐落在这条直线上。
这n个城市的快递仓都装满了货物,所以货物交换只能发生在相邻两个城市之间(比如,第2个城市只能和第1个和第3个城市直接交换)。交换的代价为两个城市货物的重量的和的平方,即若质量分别为a,b,那么代价就为(a+b)^2。
每一个城市的快递仓的货物都会发往另外一个城市(可能是自己),但不会有两个城市的货物目的地相同。
为了节约成本,请你输出所有城市的货物都发往需要到达的城市的最小总代价,答案对10 ^ 9+7取模。(指最小的答案对于10 ^ 9+7取模,并不是模意义下的最小值)
输入格式
第一行,输入一个正整数n。
第二行,输入n个以空格隔开的整数wi,表示第i个城市的货物质量为wi。
第三行,输入n个以空格隔开的正整数pi,表示第i个城市的货物要发往城市pi。
数据满足pi为1~~n的一个排列,即两两互不相同。
输出格式
输出共一行,一个整数,表示所有货物都发往自己想要到达的城市的最小代价,答案对10^9+7取模。
样例
输入/输出例子1
输入
5
5 5 4 7 6
1 2 4 3 5
输出
121
输入/输出例子2
输入
5
5 5 4 7 6
2 3 4 5 1
输出
511
数据范围
对于40%的数据,1<n ≤2000,1 ≤wi≤1000;
对于100%的数据,1≤n ≤5*10^5,1 ≤wi≤1000000。
方法
使用归并,加上前缀和优化,再加上数学二项式定理,就可以搞定
// T4
// Created by 老徐 on 2022/7/27.
// 版本0.3.0
#include <iostream>
using namespace std;
#define MOD 1000000007
typedef long long ll;
#define int ll
ll N;
class Node {
public:
int W, P;
bool operator<(const Node &A) const {
if (P < A.P) return true;
else return false;
}
bool operator>(const Node &A) const {
if (P > A.P) return true;
else return false;
}
bool operator==(const Node &A) const {
if (P == A.P) return true;
else return false;
}
} Arr[500005], Tmp[500005];
int SumTmp[500005],SumTmpTmp[500005];
int Merge(int L, int R) {
int M = (L + R) / 2;
SumTmp[L-1]=0;
SumTmpTmp[L-1]=0;
for (int i = L; i < R; i++) {
Tmp[i] = Arr[i];
SumTmp[i]=SumTmp[i-1]+Tmp[i].W;
SumTmpTmp[i]=SumTmpTmp[i-1]+(Tmp[i].W*Tmp[i].W);
}
int Result = 0;
for (int i = L, j = M, Id = L; i < M || j < R; Id++) {
if (i < M && j < R) {
if (Tmp[i] < Tmp[j]) {
Arr[Id] = Tmp[i];
i++;
} else {
Arr[Id] = Tmp[j];
//Result += Sum(i,M, j);//单调性,从Tmp[L]到Tmp[i]都比Tmp[j]小
Result+=Tmp[j].W*Tmp[j].W*(M-i)+Tmp[j].W*(SumTmp[M-1]-SumTmp[i-1])*2+SumTmpTmp[M-1]-SumTmpTmp[i-1];
//Result+=(M-i);
Result %= MOD;
j++;
}
}
else{
if(i<M) {
Arr[Id]=Tmp[i];
i++;
}
if(j<R){
Arr[Id]=Tmp[j];
j++;
}
}
}
return Result;
}
int MergeSort(int L, int R) {
if (R - L == 1) return 0;
int M = (L + R) / 2;
int Ans=0;
Ans+=MergeSort(L, M);
Ans+=MergeSort(M, R);
Ans+= Merge(L, R);
Ans%=MOD;
return Ans;
}
signed main() {
cin >> N;
for (int i = 1; i <= N; i++) {
cin >> Arr[i].W;
}
for (int i = 1; i <= N; i++) {
cin >> Arr[i].P;
}
cout << MergeSort(1, N + 1) << '\n';
return 0;
}
最后我看了老师的课,想到了前缀和,就搞定了。