【Luogu】 P8500 [NOI2022] 冒泡排序

题目链接

点击打开链接

题目解法

一个一个性质考虑,然后得出正解
首先考虑把冒泡排序的交换次数转化为逆序对数

性质B

结论1: 没有限制的序列填的数一定单调不降
证明:考虑 a i > a j ( i < j ) a_i>a_j(i<j) ai>aj(i<j) 时交换 i , j i,j i,j,可以让 i , j i,j i,j 之间大于 a j a_j aj 小于 a i a_i ai 的数产生的逆序对贡献消失,而其他逆序对数的不变的,所以不会变劣

考虑对于一个没有限制的位置 i i i 的最优值(这里只是单独考虑它)
可以抽象一下问题,把限制按 V V V 从小到大排序,把下标小于 i i i 的数看做 A A A,下标大于 i i i 的数看做 B B B
那么根据结论1, i i i 只会和有限制的数产生贡献,其贡献 ( V < a i V<a_i V<ai B B B 的个数) + + + v > a i v>a_i v>ai A A A 的个数),其中的最小值便是单独考虑 i i i 位置的最优值

结论2: 单独考虑位置的最优值一定是单调不降的
因为随着 i i i 增大, A A A 会变多, B B B 会变少,不难发现 a i a_i ai 一定会上升,使 V < a i V<a_i V<ai B B B 的个数 和 v > a i v>a_i v>ai A A A 的个数 达到一种平衡

然后就可以用线段树维护了,时间复杂度 O ( n l o g n ) O(nlogn) O(nlogn)

性质C

不难发现 a L = V a_L=V aL=V 时是最优的,否则可以交换
现在限制变成了有一些位置固定,一些位置只能 ≥ V i \ge V_i Vi
考虑贪心:从大到小枚举每个位置,先只考虑一些位置 ≥ V i \ge V_i Vi 的限制(包括固定的位置),然后得出当前位置的精确最小值,然后再用精确最小值去更新前面的位置
证明不会
用线段树维护,时间复杂度 O ( n l o g n ) O(nlogn) O(nlogn)

正解

考虑将 V V V 从大到小排序,这样可以使做到 i i i 时,前面的限制不用变
考虑对于相同的 V V V,将 l l l 从大到小排序,然后尽量选最开头的,如果一个区间已经有位置被选过了就跳过,然后把这个区间都标记 ≥ V \ge V V,使 V ′ < V V'<V V<V 的区间不能选 V ′ ′ = V V''=V V′′=V 的区间的位置
显然,这样可以使相同的 V V V 限制最少的位置
不合法的情况是一个区间无法选 = V =V =V 的最小位置,即前面 V ′ > V V'>V V>V 的区间已经把整个 V ′ ′ = V V''=V V′′=V 的区间全部标记过了
然后就变成了性质 C C C 的问题
时间复杂度 O ( n l o g n ) O(nlogn) O(nlogn)

代码里面性质 B B B 和 性质 C C C 单独的部分都有

#include <bits/stdc++.h>
#define lowbit(x) x&-x
using namespace std;
typedef long long LL;
typedef pair<int,int> pii;
const int N=1000100;
struct Seg{
    int l,r,v;
}limit[N];
int n,m;
inline int read(){
	int FF=0,RR=1;
	char ch=getchar();
	for(;!isdigit(ch);ch=getchar()) if(ch=='-') RR=-1;
	for(;isdigit(ch);ch=getchar()) FF=(FF<<1)+(FF<<3)+ch-48;
	return FF*RR;
}
namespace solveB{
    int disc[N];
    bool cmp(const Seg &x,const Seg &y){ return x.l<y.l;}
    struct SegmentTree{
        int seg[N<<2],tag[N<<2];
        void clear(int len){ for(int i=1;i<=len;i++) seg[i]=tag[i]=0;}
        void pushdown(int x){
            seg[x<<1]+=tag[x],tag[x<<1]+=tag[x];
            seg[x<<1^1]+=tag[x],tag[x<<1^1]+=tag[x];
            tag[x]=0;
        }
        void modify(int l,int r,int x,int L,int R,int val){
            if(L<=l&&r<=R){ seg[x]+=val,tag[x]+=val;return;}
            pushdown(x);
            int mid=(l+r)>>1;
            if(mid>=L) modify(l,mid,x<<1,L,R,val);
            if(mid<R) modify(mid+1,r,x<<1^1,L,R,val);
            seg[x]=min(seg[x<<1],seg[x<<1^1]); 
        }
    }sg;
    struct BinaryTree{
        int tr[N];
        void add(int x,int val,int lim){ for(;x<=lim;x+=lowbit(x)) tr[x]+=val;}
        int ask(int x){
            int res=0;
            for(;x;x-=lowbit(x)) res+=tr[x];
            return res;
        }
    }tr;
    void solve(){
        for(int i=1;i<=m;i++) disc[i]=limit[i].v;
        sort(disc+1,disc+m+1);
        int cnt=unique(disc+1,disc+m+1)-disc-1;
        for(int i=1;i<=m;i++) limit[i].v=lower_bound(disc+1,disc+cnt+1,limit[i].v)-disc;
        sort(limit+1,limit+m+1,cmp);
        int j=1;
        for(int i=2;i<=m;i++){
            if(limit[i].l!=limit[i-1].l) limit[++j]=limit[i];
            else if(limit[i].v!=limit[i-1].v){ puts("-1");return;}
        }
        m=j;
        LL ans=0;
        sg.clear(cnt<<2);
        for(int i=1;i<=m;i++) if(limit[i].v<cnt) sg.modify(1,cnt,1,limit[i].v+1,cnt,1);
        for(int i=1;i<m;i++){
            if(limit[i].v<cnt) sg.modify(1,cnt,1,limit[i].v+1,cnt,-1);
            if(limit[i].v>1) sg.modify(1,cnt,1,1,limit[i].v-1,1);
            ans+=1ll*sg.seg[1]*(limit[i+1].l-limit[i].l-1);
            // cout<<"+++"<<sg.seg[1]<<' '<<limit[i+1].l<<' '<<limit[i].l<<' '<<ans<<'\n';
        }
        // cout<<ans<<'\n';
        for(int i=m;i;i--){
            if(limit[i].v>1) ans+=tr.ask(limit[i].v-1);
            tr.add(limit[i].v,1,cnt);
        }
        for(int i=1;i<=m;i++) tr.add(limit[i].v,-1,cnt);
        printf("%lld\n",ans);
    }
    bool check(){
        for(int i=1;i<=m;i++) if(limit[i].l!=limit[i].r) return false;
        return true;
    }
}
namespace solveC{
    int disc[N],low[N],val[N];
    struct SegmentTree{
        int seg[N<<2],tag[N<<2],pos[N<<2];
        void build(int l,int r,int x){
            seg[x]=0,tag[x]=0,pos[x]=l;
            if(l==r) return;
            int mid=(l+r)>>1;
            build(l,mid,x<<1),build(mid+1,r,x<<1^1);
        }
        void pushdown(int x){
            seg[x<<1]+=tag[x],tag[x<<1]+=tag[x];
            seg[x<<1^1]+=tag[x],tag[x<<1^1]+=tag[x];
            tag[x]=0;
        }
        void modify(int l,int r,int x,int L,int R,int val){
            if(L<=l&&r<=R){ seg[x]+=val,tag[x]+=val;return;}
            pushdown(x);
            int mid=(l+r)>>1;
            if(mid>=L) modify(l,mid,x<<1,L,R,val);
            if(mid<R) modify(mid+1,r,x<<1^1,L,R,val);
            if(seg[x<<1]<=seg[x<<1^1]) pos[x]=pos[x<<1];
            else pos[x]=pos[x<<1^1];
            seg[x]=min(seg[x<<1],seg[x<<1^1]); 
        }
        pii query(int l,int r,int x,int L,int R){
            if(L<=l&&r<=R) return make_pair(seg[x],pos[x]);
            pushdown(x);
            int mid=(l+r)>>1;
            if(mid>=L&&mid<R){
                pii tl=query(l,mid,x<<1,L,R),tr=query(mid+1,r,x<<1^1,L,R);
                if(tl.first<tr.first) return tl;
                else return tr;
            }
            if(mid>=L) return query(l,mid,x<<1,L,R);
            return query(mid+1,r,x<<1^1,L,R);
        }
    }sg;
    struct BinaryTree{
        int tr[N];
        void add(int x,int val,int lim){ for(;x<=lim;x+=lowbit(x)) tr[x]+=val;}
        int ask(int x){
            int res=0;
            for(;x;x-=lowbit(x)) res+=tr[x];
            return res;
        }
    }tr;
    void solve(){
        for(int i=1;i<=m;i++) disc[i]=limit[i].v;
        sort(disc+1,disc+m+1);
        for(int i=1;i<=m;i++) limit[i].v=lower_bound(disc+1,disc+m+1,limit[i].v)-disc;
        for(int i=1;i<=n;i++) low[i]=val[i]=-1;
        for(int i=1;i<=m;i++){
            val[limit[i].l]=limit[i].v;
            for(int j=limit[i].l;j<=limit[i].r;j++) low[j]=limit[i].v;
        }
        sg.build(1,m,1);
        for(int i=1;i<=n;i++) if(low[i]>1) sg.modify(1,m,1,1,low[i]-1,1);
        for(int i=n;i;i--){
            if(low[i]>1) sg.modify(1,m,1,1,low[i]-1,-1);
            if(val[i]==-1) val[i]=sg.query(1,m,1,low[i],m).second;
            if(val[i]+1<=m) sg.modify(1,m,1,val[i]+1,m,1);
        }
        LL ans=0;
        for(int i=n;i;i--){
            if(val[i]>1) ans+=tr.ask(val[i]-1);
            tr.add(val[i],1,m);
        }
        for(int i=1;i<=n;i++) tr.add(val[i],-1,m);
        printf("%lld\n",ans);
    }
}
namespace SOLVE{
    int fa[N],disc[N],val[N],low[N];
    vector<pii> T[N];
    int get_father(int x){ return x==fa[x]?x:fa[x]=get_father(fa[x]);}
    struct SegmentTree{
        int seg[N<<2],tag[N<<2],pos[N<<2];
        void build(int l,int r,int x){
            seg[x]=0,tag[x]=0,pos[x]=l;
            if(l==r) return;
            int mid=(l+r)>>1;
            build(l,mid,x<<1),build(mid+1,r,x<<1^1);
        }
        void pushdown(int x){
            seg[x<<1]+=tag[x],tag[x<<1]+=tag[x];
            seg[x<<1^1]+=tag[x],tag[x<<1^1]+=tag[x];
            tag[x]=0;
        }
        void modify(int l,int r,int x,int L,int R,int val){
            if(L<=l&&r<=R){ seg[x]+=val,tag[x]+=val;return;}
            pushdown(x);
            int mid=(l+r)>>1;
            if(mid>=L) modify(l,mid,x<<1,L,R,val);
            if(mid<R) modify(mid+1,r,x<<1^1,L,R,val);
            if(seg[x<<1]<=seg[x<<1^1]) pos[x]=pos[x<<1];
            else pos[x]=pos[x<<1^1];
            seg[x]=min(seg[x<<1],seg[x<<1^1]); 
        }
        pii query(int l,int r,int x,int L,int R){
            if(L<=l&&r<=R) return make_pair(seg[x],pos[x]);
            pushdown(x);
            int mid=(l+r)>>1;
            if(mid>=L&&mid<R){
                pii tl=query(l,mid,x<<1,L,R),tr=query(mid+1,r,x<<1^1,L,R);
                if(tl.first<tr.first) return tl;
                else return tr;
            }
            if(mid>=L) return query(l,mid,x<<1,L,R);
            return query(mid+1,r,x<<1^1,L,R);
        }
    }sg;
    struct BinaryTree{
        int tr[N];
        void add(int x,int val,int lim){ for(;x<=lim;x+=lowbit(x)) tr[x]+=val;}
        int ask(int x){
            int res=0;
            for(;x;x-=lowbit(x)) res+=tr[x];
            return res;
        }
    }tr;
    void solve(){
        for(int i=1;i<=n+1;i++) fa[i]=i,low[i]=val[i]=-1;
        for(int i=1;i<=m;i++) disc[i]=limit[i].v;
        sort(disc+1,disc+m+1);
        for(int i=1;i<=m;i++) limit[i].v=lower_bound(disc+1,disc+m+1,limit[i].v)-disc;
        for(int i=1;i<=m;i++) T[i].clear();
        for(int i=1;i<=m;i++) T[limit[i].v].push_back(make_pair(limit[i].l,limit[i].r));
        bool haveans=1;
        for(int i=m;i;i--){
            sort(T[i].begin(),T[i].end(),greater<pii>());
            int mxl=n+5;
            for(pii t:T[i])
                if(t.second<mxl){
                    int np=get_father(t.first);
                    if(np>t.second){ haveans=0;break;}
                    mxl=np,val[np]=i;
                }
            // cerr<<"+++";
            for(pii t:T[i]) for(int j=get_father(t.first);j<=t.second;j=get_father(j)) low[j]=i,fa[j]=j+1;
            // cerr<<"---";
        }
        if(!haveans){ puts("-1");return;}
        // for(int i=1;i<=n;i++) cout<<val[i]<<' ';cout<<'\n';
        sg.build(1,m,1);
        for(int i=1;i<=n;i++) if(low[i]>1) sg.modify(1,m,1,1,low[i]-1,1);
        for(int i=n;i;i--){
            if(low[i]>1) sg.modify(1,m,1,1,low[i]-1,-1);
            if(val[i]==-1) val[i]=sg.query(1,m,1,low[i],m).second;
            if(val[i]+1<=m) sg.modify(1,m,1,val[i]+1,m,1);
        }
        LL ans=0;
        for(int i=n;i;i--){
            if(val[i]>1) ans+=tr.ask(val[i]-1);
            tr.add(val[i],1,m);
        }
        for(int i=1;i<=n;i++) tr.add(val[i],-1,m);
        printf("%lld\n",ans);
    }
}
int main(){
    // freopen("bubble6.in","r",stdin);
    // freopen("bubble.out","w",stdout);
	int T=read();
    while(T--){
        n=read(),m=read();
        for(int i=1;i<=m;i++) limit[i].l=read(),limit[i].r=read(),limit[i].v=read();
        SOLVE::solve();
    }
	return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值