题意:给你一个数组,要你重新排序这个数组使得最终的数组前一部分单调递减,后一部分单调递增。
每次操作可以改变相邻位置的两个数。问你最小操作数。
刚开始的错误想法是把直接把数组分为两个部分,前一部分排序的操作数加上后一部分的操作数,取最小值。
正解是:对于每个数取min(前部分小于它的个数,后部分小于它的个数),加起来即是答案。
用树状数组写食用更加哦!
#include<bits/stdc++.h> using namespace std; typedef long long ll; const int maxn=1e5+5; const int mod=998244353; const double ep=1e-8; #define PLI pair<ll,int> #define pb push_back #define mk make_pair #define fi first #define se second int n,b[maxn],b1[maxn]; struct node{ int l,r,sum; }a[maxn<<2]; void build(int k,int l,int r) { a[k].l=l;a[k].r=r; a[k].sum=0; if(l==r)return; int mid=(l+r)>>1; build(k<<1,l,mid); build(k<<1|1,mid+1,r); } void pushup(int k) { a[k].sum=a[k<<1].sum+a[k<<1|1].sum; } void update(int k,int x) { if(a[k].l==x&&a[k].r==x) { a[k].sum=1;return; } int mid=(a[k].l+a[k].r)>>1; if(x<=mid)update(k<<1,x); else update(k<<1|1,x); pushup(k); } int query(int k,int l,int r) { if(a[k].l>=l&&a[k].r<=r)return a[k].sum; int mid=(a[k].l+a[k].r)>>1; int sum=0; if(l<=mid)sum=query(k<<1,l,r); if(r>mid)sum+=query(k<<1|1,l,r); return sum; } ll res,l[maxn],r[maxn]; void solve() { scanf("%d",&n); for(int i=1;i<=n;i++)scanf("%d",&b[i]),b1[i]=b[i]; sort(b1+1,b1+1+n); for(int i=1;i<=n;i++)b[i]=lower_bound(b1+1,b1+1+n,b[i])-b1; build(1,1,n); for(int i=1;i<=n;i++) { int k=query(1,1,b[i]); l[i]=k; update(1,b[i]); } build(1,1,n); for(int i=n;i>=1;i--) { int k=query(1,1,b[i]); r[i]=k; update(1,b[i]); } res=0; for(int i=1;i<=n;i++) { res=res+min(l[i],r[i]); } printf("%lld ",res); } int main() { int T=1; // scanf("%d",&T); while(T--)solve(); }