hiho1996 : 01匹配 线段树
https://hihocoder.com/problemset/problem/1996
题意
你有一个n个点的图。
第i个点有权值ai,每个点的权值只可能是1或者0。
点i和点j之间有连边,当且仅当以下两个条件满足:
i < j
ai = 1 且 aj = 0
有q次询问,每次询问给出l和r,请回答:如果我们只保留标号在[l,r]之间的点,只保留两个端点编号都在[l,r]之间的那些边,那么这个图的最大匹配是多少,请输出最大匹配数。
思路
题目描述的怪玄乎,仔细观察一下这个要求的东西是有结合律的,也就是维护答案加上区间多余的0和1的个数,就可以合并两个相邻区间。显然可以搞个线段树来做。
ps.既然搞了线段树,那实际上这个东西也是区间修改的。
代码
#include<bits/stdc++.h>
using namespace std;
#define X first
#define Y second
#define PB push_back
#define MP make_pair
#define MEM(a,b) memset(a,b,sizeof(a))
typedef long long ll;
const ll mod = 1e9+7;
const int maxn =3e6+10;
int sum[maxn][2],sit[maxn];
inline int lson(int x){return x*2;}
inline int rson(int x){return x*2+1;}
void push_up(int rt){
sit[rt]=sit[lson(rt)]+sit[rson(rt)];
sum[rt][0]=sum[lson(rt)][0]+sum[rson(rt)][0];
sum[rt][1]=sum[lson(rt)][1]+sum[rson(rt)][1];
if(sum[lson(rt)][1]>0&&sum[rson(rt)][0]>0){
sit[rt]+=min(sum[lson(rt)][1],sum[rson(rt)][0]);
sum[rt][1]-=min(sum[lson(rt)][1],sum[rson(rt)][0]);
sum[rt][0]-=min(sum[lson(rt)][1],sum[rson(rt)][0]);
}
}
void build(int rt,int L,int R,vector<int> &a){
if(L==R){
sit[rt]=0;
sum[rt][a[L]]++;
return;
}
int mid=(L+R)/2;
build(lson(rt),L,mid,a);
build(rson(rt),mid+1,R,a);
push_up(rt);
}
pair<int,pair<int,int>> query(int rt,const int l,const int r,int L,int R){
if(L>=l&&R<=r) return {sit[rt],{sum[rt][0],sum[rt][1]}};
if(l>R||r<L) return {0,{0,0}};
int mid=(L+R)/2;
auto ll=query(lson(rt),l,r,L,mid);
auto rr=query(rson(rt),l,r,mid+1,R);
if(ll.X==-1) return rr;
else if(rr.X==-1) return ll;
else{
pair<int,pair<int,int>> ret;
ret.X=ll.X+rr.X;
ret.Y.X=ll.Y.X+rr.Y.X;
ret.Y.Y=ll.Y.Y+rr.Y.Y;
int mi=min(ll.Y.Y,rr.Y.X);
ret.Y.X-=mi;
ret.Y.Y-=mi;
ret.X+=mi;
return ret;
}
}
int main(){
int n,m,l,r;
cin>>n;
vector<int> a(n+1,0);
for(int i=1;i<=n;i++) cin>>a[i];
build(1,1,n,a);
cin>>m;
while(m--){
cin>>l>>r;
cout<<query(1,l,r,1,n).X<<endl;
}
return 0;
}