题目链接
点击打开链接
题目解法
一个一个性质考虑,然后得出正解
首先考虑把冒泡排序的交换次数转化为逆序对数
性质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;
}