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