信息学进阶之路

信息学进阶之路

二项式定理

(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;
}

最后我看了老师的课,想到了前缀和,就搞定了。

  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值