zoukankan      html  css  js  c++  java
  • 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
    ",++cases,cnt0+f[0]);
      }
      return 0;
    }
  • 相关阅读:
    Spark基础
    flink杂记
    算法
    算法小结
    Java内存模型
    LeetCode---Backtracking && DP
    LeetCode---Sort && Segment Tree && Greedy
    LeetCode---Depth-first && Breadth-first
    LeetCode---Bit Manipulation && Design
    MD5
  • 原文地址:https://www.cnblogs.com/liu-runda/p/6621094.html
Copyright © 2011-2022 走看看