[NOI2022]冒泡排序

题意:
n n n个数, m m m个限制,第 i i i个限制要求在 [ L i , R i ] [L_i,R_i] [Li,Ri]的最小值为 V i V_i Vi,你要得到最小的逆序对数。
数据满足 n , m < = 1 0 6 n,m<=10^6 n,m<=106

思路:
最近感觉自己的实力厚度有点不够,所以打算多做一点oi题目,来提高实力厚度。

这道题很容易想到把限制按照V从小到大排序,然后不断给整个区间修改为 V i V_i Vi,现在设修改完以后每个点的值为 b i b_i bi
操作完就可以知道某个点会对哪一个V的限制起作用。

我们把每个点按照 b i b_i bi分类,然后考虑保留哪些 b i b_i bi,修改哪些 b i b_i bi。考虑最小的 b b b,可以发现我们肯定要选择最少点不改变,而且都要尽量靠前,这样对后面的影响最小。然后考虑从小到达枚举 b b b,因为大的会覆盖小的,大的 b b b如果被小的 b b b分开,那么大的一定是相互独立的,所以我们可以决定出要选哪一些位置。

然后就是考虑要怎么填数才能使得逆序对数 最少。然后我就想了一个不太严谨的贪心:小的b一定比大的b能够变化的空间更多,因此我们一定是先处理大的b,在用小的b去将就大的b。所以我们就先填大的,再填小的。然后我们可以发现,空的格子里面填的最优的数一定是从小到大的,否则交换能得到更优的结果。而且,加入我给某个格子填了i,那么这个最优的值一定是把左边连续的若干个变成i,而不会改变更大的最优的数的格子。

我们用一个栈来维护这个最优的数,然后用一个线段树来维护应该改变左边的哪一部分。这个线段树维护当前最优解和改成bi之后逆序对数的差值,只需要在确定某个点的之以后把左边增加1,右边减少1即可(类似)。

感觉这个题目的思路特别的绕。

#include<bits/stdc++.h>
#define rep(i,x,y) for(int i=x;i<=y;i++)
#define dwn(i,x,y) for(int i=x;i>=y;i--)
#define ll long long
using namespace std;
template<typename T>inline void qr(T &x){
    x=0;int f=0;char s=getchar();
    while(!isdigit(s))f|=s=='-',s=getchar();
    while(isdigit(s))x=x*10+s-48,s=getchar();
    x=f?-x:x;
}
int cc=0,buf[31];
template<typename T>inline void qw(T x){
    if(x<0)putchar('-'),x=-x;
    do{buf[++cc]=int(x%10);x/=10;}while(x);
    while(cc)putchar(buf[cc--]+'0');
}
const int N=1e6+10;
struct SGT{
    int l,r,c,lazy;bool clr;
}t[N<<2];
#define lc p<<1
#define rc p<<1|1
#define mid (t[p].l+t[p].r)/2
void update(int p){
    t[p].c=max(t[lc].c,t[rc].c);
}
void pushdown(int p){
    if(t[p].clr){
        t[lc].c=t[lc].lazy=0;
        t[lc].clr=1;
        t[rc].c=t[rc].lazy=0;
        t[rc].clr=1;
        t[p].clr=0;
    }
    if(t[p].lazy){
        t[lc].c+=t[p].lazy;
        t[lc].lazy+=t[p].lazy;
        t[rc].c+=t[p].lazy;
        t[rc].lazy+=t[p].lazy;
        t[p].lazy=0;
    }
}
void build(int p,int l,int r){
    t[p].l=l,t[p].r=r;
    t[p].c=0;
    t[p].lazy=0;
    t[p].clr=0;
    if(l<r){
        build(lc,l,mid);
        build(rc,mid+1,r);
    }
}
void change(int p,int x,int y,int k){
    if(x<=t[p].l&&t[p].r<=y){
        t[p].c+=k;
        t[p].lazy+=k;
        return;
    }
    pushdown(p);
    if(x<=mid)change(lc,x,y,k);
    if(mid<y)change(rc,x,y,k);
    update(p);
}
void change2(int p,int x,int y){
    if(x<=t[p].l&&t[p].r<=y){
        t[p].c=t[p].lazy=0;
        t[p].clr=1;
        return;
    }
    pushdown(p);
    if(x<=mid)change2(lc,x,y);
    if(mid<y)change2(rc,x,y);
    update(p);
}
int query(int p){
    if(t[p].l==t[p].r){
        if(t[p].c>=0)return 0;
        return t[p].l;
    }
    pushdown(p);
    if(t[lc].c<0)return max(t[lc].r,query(rc));
    else return query(lc);
}
int n,m;
int cnt,s[N];
struct node{
    int x,y,z;
    node(int xx=0,int yy=0,int zz=0):x(xx),y(yy),z(zz){}
}a[N],sta[N];int tp;
vector<int>zuo[N],you[N];
int b[N];//the max num cover i
vector<int>pos[N],seg[N];//pos was valued i; segment have value i
bool used[N];
int ans[N];
int c[N];
bool cmp1(int p1,int p2){
    return p1<p2;
}
bool cmp2(int p1,int p2){
    if(a[p1].x!=a[p2].x)return a[p1].x<a[p2].x;
    return a[p1].y>a[p2].y;
}
void add(int x){
    for(;x<=n;x+=x&-x)c[x]++;
}
int calc(int x){
    int ret=0;
    for(;x;x-=x&-x)ret+=c[x];
    return ret;
}
int testcase;
void solve(){
    qr(n),qr(m);
    rep(i,1,n){
        zuo[i].clear();
        you[i].clear();
    }
    cnt=0;
    rep(i,1,m){
        qr(a[i].x),qr(a[i].y),qr(a[i].z);
        s[++cnt]=a[i].z;
    }
    sort(s+1,s+cnt+1);
    cnt=unique(s+1,s+cnt+1)-s-1;
    rep(i,1,m){
        a[i].z=lower_bound(s+1,s+cnt+1,a[i].z)-s;
        zuo[a[i].x].push_back(i);
        you[a[i].y].push_back(i);
    }
    multiset<int>cover;
    rep(i,1,cnt){
        pos[i].clear();
        seg[i].clear();
    }
    rep(i,1,m)seg[a[i].z].push_back(i);
    rep(i,1,n){
        for(int id:zuo[i])cover.insert(a[id].z);
        b[i]=0;
        if(cover.size()){
            b[i]=*(--cover.end());
            pos[b[i]].push_back(i);
        }
        for(int id:you[i])cover.erase(cover.find(a[id].z));
    }
    rep(i,1,n)used[i]=0;
    bool flag=1;
    rep(i,1,cnt){
        sort(pos[i].begin(),pos[i].end(),cmp1);
        sort(seg[i].begin(),seg[i].end(),cmp2);
        int now=(int)pos[i].size()-1,last=n+1;
        for(int j=(int)seg[i].size()-1;j>=0;j--){
            node t=a[seg[i][j]];
            if(last<=t.y)continue;
            while(now>0&&pos[i][now-1]>=t.x)now--;
            if(now<0||pos[i][now]>t.y||pos[i][now]<t.x){
                flag=0;break;
            }
            last=pos[i][now];
            used[last]=1;
        }
        if(!flag)break;
    }
    if(!flag){
        puts("-1");
        return;
    }
    build(1,1,n);
    sta[tp=1]=node(1,n,cnt+1);
    dwn(i,cnt,1){
        for(int x:pos[i]){
            if(used[x]){
                ans[x]=i;
                if(x>1)change(1,1,x-1,-1);
            }
        }
        int len=query(1);
        if(len){
            while(tp&&sta[tp].y<=len)tp--;
            if(tp){
                sta[tp].x=len+1;
            }
            sta[++tp]=node(1,len,i);
            change2(1,1,len);
        }
        for(int x:pos[i]){
            if(!used[x]){
                int l=1,r=tp,Mid,pos;
                while(l<=r){
                    Mid=(l+r)/2;
                    if(sta[Mid].x<=x)pos=Mid,r=Mid-1;
                    else l=Mid+1;
                }
                ans[x]=sta[pos].z;
                if(x<n)change(1,x+1,n,1);
            }
        }
        for(int x:pos[i]){
            if(used[x]){
                if(x<n)change(1,x+1,n,1);
            }
        }
    }
    rep(x,1,n)if(!b[x]){
        int l=1,r=tp,Mid,pos;
        while(l<=r){
            Mid=(l+r)/2;
            if(sta[Mid].x<=x)pos=Mid,r=Mid-1;
            else l=Mid+1;
        }
        ans[x]=sta[pos].z;
    }
    rep(i,1,n)c[i]=0;
    ll tot=0;
    dwn(i,n,1){
        tot+=calc(ans[i]-1);
        add(ans[i]);
    }
    qw(tot);puts("");
}
int main(){
    // freopen("bubble6.in","r",stdin);
    // freopen("bubble6.out","w",stdout);
    int tt;qr(tt);
    while(tt--)solve();
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值