题意:
给出个水箱,水箱两侧有无限高的隔板,水箱内有整数高度的隔板将水箱分成n-1份,现在给出m个限制,每个限制表示某个位置的某个高度有水或没水,问最多能同时满足多少个限制.n,m<=2*10^5
分析:
“某个位置有水”的限制会导致从这个位置向两侧扩展的一个区间都有这个高度的水.只要找到从这个位置向左/向右第一个比这个限制的水位高的挡板即可,可以用单调栈+二分O(nlogn)求出所有区间,每个区间对应有一个高度(就是这个限制的水位高度).如果某个有水的区间对应的高度下方有一个位置存在”这个位置不能有水”的限制,那么这两个不同种类的限制就会产生矛盾,两个有矛盾的限制不能同时满足,一些限制如果两两不存在矛盾那么就可以同时满足,而且只有不同种类的限制才会产生矛盾,那么这是一个二分图最大独立集的模型,我们就可以打一个网络流暴力
仔细分析一下,我们发现两个不同的有水的限制所对应的区间要么相互包含,要么相互没有公共部分,要么完全相同.假设出现了相交但不包含的情况,不妨令两个区间为[l1,r1]和[l2,r2],且l1<l2<r1<r2,那么l1和r1一定比[l1,r1]内部的挡板高,l2和r2一定比[l2,r2]内部的挡板高,第一个限制如果越过了l2,就不应该被r1挡住,第二个限制如果越过了r1,就不应该被l2挡住.因此所有区间要么相互包含要么相互独立.
这样,所有区间的包含关系形成了一个森林结构,可以加一个超级根连成一棵树.
那么我们如果让某个有水区间的限制满足,那么这个区间所"包含(即:横坐标范围不超出这个区间且高度在这个区间下方)"的所有区间都必须有水,而且这个区间下方所有的"没水"的限制都不能满足.最后我们肯定会选出某些子树,满足这些子树内的"有水"限制且放弃这些子树的根节点覆盖的"无水"限制(根节点的区间的覆盖范围是最大的).
那么将所有限制按高度排序,扫描一遍,用树状数组求出每个区间覆盖的"无水"限制的个数,用平衡树维护一下区间,把树形结构建立出来就可以了.
#include<cstdio> #include<vector> #include<cstring> #include<algorithm> #include<cstdlib> #include<cctype> using namespace std; void read(int &x){ char ch;while(ch=getchar(),!isdigit(ch)); x=ch-'0'; while(ch=getchar(),isdigit(ch))x=x*10+ch-'0'; } const int maxn=200005; vector<int> num[maxn]; int n,m; int h[maxn]; int l[maxn],r[maxn]; int x[maxn],y[maxn],typ[maxn]; int binary(int *stk,int l,int r,int x){ while(l<=r){ int mid=(l+r)>>1; if(h[stk[mid]]>=x)l=mid+1; else r=mid-1; } return stk[l-1]; } void init(){ static int stk[maxn],top=0; top=0; for(int i=1;i<=n;++i){ num[i].clear(); } for(int i=1;i<=m;++i){ if(typ[i]==1)num[x[i]].push_back(i); } stk[top++]=0; for(int i=1;i<=n;++i){ for(vector<int>::iterator pt=num[i].begin();pt!=num[i].end();++pt){ l[(*pt)]=binary(stk,0,top-1,y[*pt]+1); } while(h[stk[top-1]]<h[i]){ top--; } stk[top++]=i; } top=0;stk[top++]=n; for(int i=n;i>=1;--i){ for(vector<int>::iterator pt=num[i].begin();pt!=num[i].end();++pt){ r[*pt]=binary(stk,0,top-1,y[*pt]+1); } while(h[stk[top-1]]<h[i-1]){ top--; } stk[top++]=i-1; } } int sum[maxn];//sum[i]:limitation i of typ1 contain how many limatation of typ0 int c[maxn]; void add(int x){ for(;x<maxn;x+=x&(-x))c[x]++; } int pre(int x){ int ans=0; for(;x;x-=x&(-x))ans+=c[x]; return ans; } int seq[maxn]; bool cmp(const int &a,const int &b){ return (y[a]==y[b])?typ[a]<typ[b]:y[a]<y[b]; } struct edge{ int to,next; }lst[maxn<<1];int first[maxn],len=1; void addedge(int a,int b){ lst[len].to=b;lst[len].next=first[a];first[a]=len++; } struct node{ int l,r,num,ord,sz; node* ch[2]; node(){} node(int L,int R,int N){ ch[0]=ch[1]=0; l=L;r=R;num=N;ord=rand();sz=1; } void update(){ sz=1; if(ch[0])sz+=ch[0]->sz; if(ch[1])sz+=ch[1]->sz; } }t[maxn*2];int tsz=0; node* newnode(int L,int R,int x){ t[++tsz]=node(L,R,x);return t+tsz; } void rot(node* &rt,int t){ node* c=rt->ch[t];rt->ch[t]=c->ch[t^1];c->ch[t^1]=rt;rt=c; c->ch[t^1]->update();c->update(); } void Insert(node* &rt,int x){ if(!rt)rt=newnode(l[x]+1,r[x],x); else{ int t=(l[x]+1)>rt->l; Insert(rt->ch[t],x); rt->update(); if(rt->ch[t]->ord>rt->ord)rot(rt,t); } } node* succ(node* rt,int x){ if(!rt)return 0; if(rt->l>=l[x]+1){ node* tmp=succ(rt->ch[0],x); if(tmp)return tmp; else return rt; }else{ return succ(rt->ch[1],x); } } node* root; void remove(node* &rt,int l){ if(rt->l!=l){ remove(rt->ch[l>rt->l],l); rt->update(); }else{ if(!rt->ch[0])rt=rt->ch[1]; else if(!rt->ch[1])rt=rt->ch[0]; else{ if(rt->ch[0]->ord>rt->ch[1]->ord){ rot(rt,0);remove(rt->ch[1],l); }else{ rot(rt,1);remove(rt->ch[0],l); } rt->update(); } } } void traverse(node* rt){ if(!rt)return; addedge(0,rt->num); traverse(rt->ch[0]);traverse(rt->ch[1]); } int sz[maxn],f[maxn]; void dp(int x){ if(x)sz[x]=1; else sz[x]=0; int tmp=0; for(int pt=first[x];pt;pt=lst[pt].next){ dp(lst[pt].to);sz[x]+=sz[lst[pt].to]; tmp+=max(f[lst[pt].to],0); } f[x]=max(tmp,sz[x]-sum[x]); } int main(){ int cases=0; int tests;read(tests); while(tests--){ root=0;tsz=0; memset(c,0,sizeof(c)); memset(first,0,sizeof(first));len=1; read(n);read(m); int cnt0=0; for(int i=1;i<n;++i)scanf("%d",&h[i]); for(int i=1;i<=m;++i){ read(x[i]);read(y[i]);read(typ[i]); if(typ[i]==0)cnt0++; } h[0]=h[n]=0x7f7f7f7f; init(); for(int i=1;i<=m;++i)seq[i]=i; sort(seq+1,seq+m+1,cmp); for(int i=1;i<=m;++i){ if(typ[seq[i]]==0)add(x[seq[i]]); else{ sum[seq[i]]=pre(r[seq[i]])-pre(l[seq[i]]); node* pt; while((pt=succ(root,seq[i]))&&pt->r<=r[seq[i]]){ addedge(seq[i],pt->num); remove(root,pt->l); } Insert(root,seq[i]); } } sum[0]=pre(n); traverse(root); dp(0); printf("Case #%d: %d ",++cases,cnt0+f[0]); } return 0; }