洛谷P1937 [USACO10MAR] Barn Allocation G
洛谷P1937 [USACO10MAR] Barn Allocation G
怎么说呢,这题没啥难点,主要考察线段树+端点排序贪心Trick。
这里重点讲一下这个Trick。
Lemma
对于一段序列,给出 \(N\) 个区间,选出 \(M\) 个不重叠的区间,使 \(M\) 最大化。
此时对区间进行右端点升序排序,贪心选择,有最大 \(M\) 。
证明:
首先对于一个区间 \([L,R]\) ,其他区间 \(S\) 有 \(4\) 种情况。
- \(S\) \(\in\) \([L,R]\) ,此时优先选择 \(S\) ,因为 \(S\) 更小。
- \([L,R]\) \(\in\) \(S\) ,此时优先选择 \([L,R]\) ,因为 \([L,R]\) 更小。
- \(S\) 与 \([L,R]\) 左端重叠,选 \(S\) ,这样可以使选到的区间右端点提前。
- \(S\) 与 \([L,R]\) 右端重叠,选 \([L,R]\) 。
补一张图:
就是这样。
\(\huge \mathscr {Code}\)
#include<bits/stdc++.h>
using namespace std;
const int N = 1e5+100;
int n,m,num[N],ans;
struct node{
int lval,rval,mmin,tag;
}T[N<<2];
inline void pushup(int u){
T[u].mmin = min(T[u*2].mmin,T[u*2+1].mmin);
}
inline void pushdown(int u){
if(T[u].tag){
T[u*2].mmin -= T[u].tag,T[u*2+1].mmin -= T[u].tag;
T[u*2].tag += T[u].tag,T[u*2+1].tag += T[u].tag;
T[u].tag = 0;
}
}
inline void build(int u,int l,int r){
T[u].lval = l,T[u].rval = r;
if(l==r){
T[u].mmin = num[l];
return;
}
int mid = (T[u].lval+T[u].rval)>>1;
build(u*2,l,mid),build(u*2+1,mid+1,r);
pushup(u);
}
inline void update(int u,int ql,int qr,int v){
if(ql<=T[u].lval && T[u].rval<=qr){
T[u].tag += v;
T[u].mmin -= v;
return;
}
pushdown(u);
int mid = (T[u].lval+T[u].rval)>>1;
if(ql<=mid) update(u*2,ql,qr,v);
if(qr>mid) update(u*2+1,ql,qr,v);
pushup(u);
}
int query(int u,int ql,int qr){
if(ql<=T[u].lval && T[u].rval<=qr) return T[u].mmin;
pushdown(u);
int mid = (T[u].lval+T[u].rval)>>1,ans = 1e9;
if(ql<=mid) ans = min(ans,query(u*2,ql,qr));
if(qr>mid) ans = min(ans,query(u*2+1,ql,qr));
return ans;
}
struct dat{
int l,r;
friend bool operator<(dat a,dat b) {return a.r<b.r;}
}seq[N];
signed main(){
ios::sync_with_stdio(false);
cin.tie(0),cout.tie(0);
cin>>n>>m;
for(int i=1;i<=n;i++) cin>>num[i];
build(1,1,n);
for(int i=1;i<=m;i++) cin>>seq[i].l>>seq[i].r;
sort(seq+1,seq+m+1);
for(int i=1;i<=m;i++){
if(query(1,seq[i].l,seq[i].r)){
update(1,seq[i].l,seq[i].r,1);
ans++;
}
}
cout<<ans;
return 0;
}

浙公网安备 33010602011771号