链接:https://www.luogu.org/problemnew/show/P1966
这题我糊了一个假结论就过去了,也不知道这个结论为什么是对的。。首先sigma((ai-bi)^2)可以拆开,那么ai^2+bi^2是个常量,而求这个最小就是sigma(-1*ai*bi)最小即sigma(ai*bi)最大,那么贪心的想一想a数组中最大的数让它被计算尽可能多次,也就是想必要和b数组中最大的对应,那么次大、次次大...也是同理,也就是要让a数组某一排名的和b数组某一排名的对上(注意可能有相同的排名)。
接下来就是我和标算不一样的地方。。
标算离散化a,b数组,然后按a为关键字排b,显然之后为了一一对应就是求逆序对数了,虽然个人觉得有点难想到,但正确性一目了然,很好很优越。。
我的呢,先猜了一波想必在a里交换和在b里交换是一样的,那么就只用考虑a序列。然后呢,再猜一波可以贪心从左往右考虑,就是从第一位开始向右枚举,找到a序列中这个位置及右侧第一个与b这个位置对应的,计算它交换过来的贡献,然后因为它交换的路径上的点右移了1,就用树状数组记录一下,计算到它们时查询被右移了多少加上去。。。虽然觉得很假,但手模一波发现貌似自己hack不掉,就信仰打了,没想到真能a。。。
// luogu-judger-enable-o2
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N=100010;
const ll mod=99999997;
struct moha{
ll x;int id;
}c[N],d[N];
int n,ps[100010];ll a[N],b[N],tr[100010];
vector<int>mp[100010];
bool cmp(moha x,moha y)
{return x.x==y.x?x.id<y.id:x.x<y.x;}
int ask(int x)
{
int res=0;
for(int i=x;i;i-=i&(-i))
res+=tr[i];
return res;
}
void add(int r)
{
for(int i=1;i<=n;i+=i&(-i))
tr[i]++;
for(int i=r+1;i<=n;i+=i&(-i))
tr[i]--;
}
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
scanf("%lld",&a[i]),c[i].x=a[i],c[i].id=i;
for(int i=1;i<=n;i++)
scanf("%lld",&b[i]),d[i].x=b[i],d[i].id=i;
sort(c+1,c+n+1,cmp),sort(d+1,d+n+1,cmp);
int tp1=0,tp2=0,nw;ll ans=0;
for(int i=1;i<=n;i++)
{
a[c[i].id]=++tp1;
b[d[i].id]=++tp2;
}
for(int i=1;i<=n;i++)ps[a[i]]=i;
for(int i=1;i<=n;i++)
{
nw=ps[b[i]];
nw+=ask(nw);
ans=(ans+(nw-i))%mod;
add(ps[b[i]]);
}
cout<<ans;
}