Discover Water Tank HDU - 5575

题意:一个底面1*n的容器,两边无限高,中间被n-1块板阻断,给你m次观测,每次观测告诉你第i个区域是否有水,问你这些观测中最多有几个是对的。

思路:如果水超过板的高度,那么板的两边的区间就可以视为一个区间。所以只要枚举水没有溢出和水已经溢出这两种状态。我们从低到高的角度遍历板子,每次处理好这种状态,然后把这两个区间合并成一个区间,最后合并成一个区间,取一个最大值即可。我们把每个区域的观测存到一个堆中,当枚举到第i个板子时,把两边低于板高的观测取出,然后合并堆就好了。

开始不会可并堆,然后去学了一波左斜树,后来lxw告诉我__gnu_pbds有封装好的。。。

pbds版:

#include <bits/stdc++.h>
#include <ext/pb_ds/assoc_container.hpp>
#include <ext/pb_ds/priority_queue.hpp>
using namespace std;
using namespace __gnu_pbds;
#define pb push_back
#define pa pair<int,int>
#define N 200005

pa a[N];
int f[N];
int dp[N][2];
int finds(int x){return x==f[x]?x:f[x]=finds(f[x]); }
int get(int x,int y){
    int w=dp[x][1];
    while(!q[x].empty()){
        pa p=q[x].top();
        if(p.first>y)break;
        w+=p.second;
        dp[x][0]=max(dp[x][0],w);
        q[x].pop();
    }
    return w;
}
int main()
{
    int T,cas=1,n,m,id,h,fl;
    cin>>T;
    while(T--){
        scanf("%d%d",&n,&m);
        for(int i=1;i<n;i++){
            scanf("%d",&a[i].first);
            a[i].second=i;
        }
        for(int i=1;i<=n;i++)f[i]=i,dp[i][0]=dp[i][1]=0;
        sort(a+1,a+n);
        for(int i=1;i<=m;i++){
            scanf("%d %d %d",&id,&h,&fl);
            q[id].push(pa(h+1,fl==1?1:-1));
            if(!fl)dp[id][0]++,dp[id][1]++;
        }
        int ans=0,w;
        for(int i=1;i<n;i++){
            int fx=finds(a[i].second),fy=finds(a[i].second+1);
            dp[fy][1]=get(fx,a[i].first)+get(fy,a[i].first);
            dp[fy][0]=dp[fx][0]+dp[fy][0];
            f[fx]=fy;q[fy].join(q[fx]);
        }
        int x=finds(1);
        dp[x][1]=get(x,1e9+7);
        printf("Case #%d: %d\n",cas++,max(dp[x][0],dp[x][1]));
    }
    return 0;
}

左斜树版:

#include <bits/stdc++.h>
#include <ext/pb_ds/assoc_container.hpp>
#include <ext/pb_ds/priority_queue.hpp>
using namespace std;
using namespace __gnu_pbds;
#define pb push_back
#define pa pair<int,int>
#define N 200005
__gnu_pbds::priority_queue<pa,greater<pa> >q[N];

int c[N],rs[N],ls[N],dis[N],d[N],tot=0,he[N];
int New(int x,int y){
    c[tot]=x;d[tot]=y;
    ls[tot]=rs[tot]=dis[tot]=0;
    return tot++;
}
int Merge(int a,int b){
    if(!a||!b)return a+b;
    if(c[a]>c[b])swap(a,b);
    rs[a]=Merge(rs[a],b);
    if(dis[ls[a]]<dis[rs[a]])swap(ls[a],rs[a]);
    dis[a]=dis[rs[a]]+1;
    return a;
}
int pop(int a){return Merge(ls[a],rs[a]);}
pa a[N];
int f[N];
int dp[N][2];
int finds(int x){return x==f[x]?x:f[x]=finds(f[x]); }
int get(int x,int y){
    int w=dp[x][1];
    while(he[x]!=0){
        if(c[he[x]]>y)break;
        w+=d[he[x]];
        dp[x][0]=max(dp[x][0],w);
        he[x]=pop(he[x]);
    }
    return w;
}
int main()
{
    int T,cas=1,n,m,id,h,fl;
    cin>>T;
    while(T--){
        scanf("%d%d",&n,&m);
        tot=1;
        for(int i=1;i<n;i++){
            scanf("%d",&a[i].first);
            a[i].second=i;
        }
        for(int i=1;i<=n;i++)f[i]=i,dp[i][0]=dp[i][1]=0,he[i]=0;
        sort(a+1,a+n);
        for(int i=1;i<=m;i++){
            scanf("%d %d %d",&id,&h,&fl);
            he[id]=!he[id]?New(h+1,fl?1:-1):Merge(he[id],New(h+1,fl?1:-1));
            if(!fl)dp[id][0]++,dp[id][1]++;
        }
        int ans=0,w;
        for(int i=1;i<n;i++){
            int fx=finds(a[i].second),fy=finds(a[i].second+1);
            dp[fy][1]=get(fx,a[i].first)+get(fy,a[i].first);
            dp[fy][0]=dp[fx][0]+dp[fy][0];
            f[fx]=fy;he[fy]=Merge(he[fx],he[fy]);
        }
        int x=finds(1);
        dp[x][1]=get(x,1e9+7);
        printf("Case #%d: %d\n",cas++,max(dp[x][0],dp[x][1]));
    }
    return 0;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值