归并排序思维题
链接
题目链接
题意
给定两个序列,每次操作可以交换同一序列相邻的数字,问最少经过多少次操作可以可以使两个序列相同位相加的平方的和最大
题解
要使和最大,一定是两个序列最大的和最大的搭配,第二的和第二的搭配等。重要的是求要换次数。要点:
- 交换次数就是求逆序对,可以使用归并排序
- 可以看作一个序列不动,另一个序列排序后和不动序列顺序一样即可。
核心代码:
for(ll i=1;i<=n;++i)
x[a[i].id]=b[i].id;
sort之后,a和b的数组顺序是按照val从小到大,id顺序表示原来序列的从小到大。这样排出来的序列是应该得到的序列。把这个序列从现在的无序排成有序就是把原来的序列排成取得最大值的序列,求他的逆序对即可。
代码
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define ull unsigned long long
inline int read()
{
char ch = getchar();int x = 0, f = 1;
while(ch<'0'||ch>'9'){if(ch == '-') f = -1;ch = getchar();}
while('0'<=ch && ch <= '9'){x = x*10+ch-'0';ch = getchar();}
return x*f;
}
ll mod=1e8+7;
ll INF=1e15;
ll Inf=0x3f3f3f3f;
struct nod
{
ll val,id;
};
bool cmp(nod q,nod w)
{
return q.val<w.val;
}
nod a[100005];
nod b[100005];
ll x[100005];
ll y[100005];
ll sum=0;
void quicksort(ll l,ll mid,ll r)
{
ll i=l;
ll j=l;
ll k=mid+1;
while(i<=mid && k<=r)
{
if(x[i]<=x[k])
y[j++]=x[i++];
else
{
sum=(ll)(sum+mid-i+1);
y[j++]=x[k++];
}
}
while(i<=mid)
y[j++]=x[i++];
while(k<=r)
y[j++]=x[k++];
for(ll s=l;s<=r;++s)
x[s]=y[s];
}
void quick(ll l,ll r)
{
if(l>=r)
return ;
ll mid=(l+r)/2;
quick(l,mid);
quick(mid+1,r);
quicksort(l,mid,r);
return ;
}
int main()
{
ios::sync_with_stdio(0);
cin.tie(0);
ll n;
scanf("%lld",&n);
for(ll i=1;i<=n;++i)
{
scanf("%lld",&a[i].val);
a[i].id=i;
}
for(ll i=1;i<=n;++i)
{
scanf("%lld",&b[i].val);
b[i].id=i;
}
sort(a+1,a+1+n,cmp);
sort(b+1,b+1+n,cmp);
ll ans=0;
for(ll i=1;i<=n;++i)
ans=(ans+(a[i].val+b[i].val)*(a[i].val+b[i].val))%mod;
for(ll i=1;i<=n;++i)
x[a[i].id]=b[i].id;
quick(1,n);
printf("%lld %lld",ans,sum);
return 0;
}