loj535. 「LibreOJ Round #6」花火

【题意】一个两两不同的序列,可以先任意交换两个数,然后就只能互换相邻的两个点,求最少要互换多少次使得原数列递增

【分析】这道题目到手很容易发现先要计算出逆序对数量,然后计算互换哪两个位置的减少逆序对数最多。

实现方式:考虑每个点表示成(i,a[i])序号为i,值为a[i]

考虑到一定要把一个左侧的较大数和一个右侧的较小数交换,且左侧的是前缀的最大,右侧的是后缀的最小,这个很容易证明(贪心)

然后问题就被转换成了一个求一个矩形内点的个数的问题,把交换的两个点作为矩形的左下角和右上角,矩形内部点的个数就是减少的逆序对的个数

然后就是利用扫描线计算矩形内部的点个数,利用差分思想,把矩形的上下边拆开,每次查询区间和即可

【代码】

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=3e5+5;
int n;
ll ans,c[maxn];
int a[maxn],lmax[maxn],rmin[maxn];
int lowbit(int x)
{
    return x&(-x);
}
void add(int x,int v)
{
    while(x<=n)
    {
        c[x]+=v;
        x+=lowbit(x);
    }
}
ll getsum(int x)
{
    ll res=0;
    while(x>0)
    {
        res+=c[x];
        x-=lowbit(x);
    }
    return res;
}
struct line
{
    int l,r,h,v;
}li[maxn<<1];
bool cmp(line a,line b)
{
    if(a.h!=b.h) return a.h<b.h;
    return a.l<b.l;
}
ll mx[maxn<<2],tg[maxn<<2];
void modify(int now,int L,int R,int l,int r,int v)
{
    if(L>R) return;
    if(L>=l && R<=r)
    {
        mx[now]+=v;
        tg[now]+=v;
        return;
    }
    int mid=L+R>>1;
    if(l<=mid) modify(now<<1,L,mid,l,r,v);
    if(mid<r) modify(now<<1|1,mid+1,R,l,r,v);
    mx[now]=max(mx[now<<1],mx[now<<1|1])+tg[now];
}
int cnt;
int main()
{
    freopen("flower.in","r",stdin);
    freopen("flower.out","w",stdout);
    scanf("%d",&n);
    for(int i=1;i<=n;i++) scanf("%d",&a[i]);
    ll ans=0;
    for(int i=1;i<=n;i++)
    {
        ans+=getsum(n)-getsum(a[i]);
        add(a[i],1);
        lmax[i]=max(lmax[i-1],a[i]);
    }
    rmin[n+1]=maxn;
    for(int i=n;i>=1;i--) rmin[i]=min(rmin[i+1],a[i]);
    for(int i=1;i<=n;i++)
    {
        int l=lower_bound(lmax+1,lmax+n+1,a[i])-lmax;
        int r=lower_bound(rmin+1,rmin+n+1,a[i])-rmin;
        if(rmin[r]>a[i]) r--;
        if(l>=i || r<=i) continue;
        li[++cnt]=(line){l,i-1,i+1,1};
        li[++cnt]=(line){l,i-1,r+1,-1};
    }
    sort(li+1,li+cnt+1,cmp);
    ll tmp=0;
    for(int i=1;i<=cnt;i++)
    {
        modify(1,1,n,li[i].l,li[i].r,li[i].v);
        tmp=max(tmp,mx[1]);
    }
    printf("%lld",ans-2LL*tmp);
    return 0;
}

 

 
posted @ 2021-05-10 22:29  andyc_03  阅读(96)  评论(0编辑  收藏  举报