hdu5575 Discover Water Tank

题意:

给出个水箱,水箱两侧有无限高的隔板,水箱内有整数高度的隔板将水箱分成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\n",++cases,cnt0+f[0]);
  }
  return 0;
}

 

posted @ 2017-03-26 08:10  liu_runda  阅读(507)  评论(0编辑  收藏  举报
偶然想到可以用这样的字体藏一点想说的话,可是并没有什么想说的. 现在有了:文化课好难