题解:AT_abc351_f [ABC351F] Double Sum

关于某c人士强制偷袭代码导致AT号被封,\({\color{red}\mathrm{警钟敲碎}}\)

题意

一个长 \(n\) 的数组 \(a\),求所有顺序对中两元素之差的和。

分析

听说有大佬2min切掉。很明显,暴力过不去。

考虑计算每个元素的贡献。设 \(id\) 为该元素之前所有比它小的元素个数,\(sum\) 表示这些元素的和。因为要求所有差值的和,即理解为这个数乘上小于它的元素个数再减去比他小的数的和。那么此元素的贡献显然就是 \(a_i\times id-sum\)

于是要维护 \(id\)\(sum\)。我们可以用桶树状数组或者动态开点权值线段树,作者懒,用了前者。开两个桶树状数组,分别存储元素个数和元素之和。对于每个元素 \(a_i\),计算结果后让第一个桶添 \(1\),第二个桶添 \(a_i\)

这道题就做完了。但是显然,由于 \(a_i\) 很大,所以需要离散化(这个应该不用讲了)。

Code

#include<bits/stdc++.h>
#define int long long
using namespace std;
inline int read()
{
	int w=1,s=0;char ch=getchar();
	while(!isdigit(ch)){if(ch=='-')w=-1;ch=getchar();}
	while(isdigit(ch)){s=s*10+(ch-'0');ch=getchar();}
	return w*s;
}
const int maxn=4e6+10;
int n;
int a[maxn],li[maxn]; 
int cid[maxn*2],csum[maxn*2];
int lb(int x){return x&-x;}
void addid(int x,int y)
{
	for(;x<=maxn;x+=lb(x))cid[x]+=y;
}
int askid(int x)
{
	int ans=0;
	for(;x;x-=lb(x))ans+=cid[x];
	return ans;
}
void addsum(int x,int y)
{
	for(;x<=maxn;x+=lb(x))csum[x]+=y;
}
int asksum(int x)
{
	int ans=0;
	for(;x;x-=lb(x))ans+=csum[x];
	return ans;
}
signed main()
{
//  freopen("xxx.in","r",stdin);
//	freopen("xxx.out","w",stdout);
	cin>>n;
	for(int i=1;i<=n;i++)
	{
		a[i]=read();
		li[i]=a[i];
	}
	sort(li+1,li+n+1);
	int tot=unique(li+1,li+n+1)-li-1,ans=0;
	for(int i=1;i<=n;i++)
	{
		int x=lower_bound(li+1,li+tot+1,a[i])-li;
		int id=askid(x-1),sum=asksum(x-1);
		ans=ans+a[i]*id-sum;
		addid(x,1);addsum(x,a[i]);
	}
	cout<<ans;
	return 0;
}

posted @ 2024-08-12 14:33  Redamancy_Lydic  阅读(6)  评论(0编辑  收藏  举报