HDU 5575 Discover Water Tank(线段树+自底向上dp+并查集)

题意

给定n个挡板和m次回答,每次回答为x号水池的H+0.5高度是否有水,问这些回答互不矛盾的最大集合。

题解

设定状态dp[i][0-1]代表第i个区间枚举到当前回答后有水的最大不矛盾集合和没水的最大不矛盾集合。1代表有水,0代表没水。
我们将回答的H从小到大排序后升序枚举,对于第i个回答,我们可以用线段树定位出其所影响的区间[l,r]在这个区间内去进行dp转移。同时我们还发现若枚举到了一定的高度,我们可以进行区间与区间的合并(由于高度是递增枚举的),规定向右合并,这里用并查集就可以维护。
之后我们就可以写出转移(设枚举到的回答有水为op=1,没水为op=0)
d p [ r ] [ 1 ] = ∑ i = l r d p [ i ] [ 1 ] + o p ? 1 : 0 dp[r][1]=\sum_{i=l}^{r}{dp[i][1]}+op?1:0 dp[r][1]=i=lrdp[i][1]+op?1:0
d p [ r ] [ 0 ] = ∑ i = l r m a x ( d p [ i ] [ 0 ] , d p [ i ] [ 1 ] ) + o p ? 0 : 1 dp[r][0]=\sum_{i=l}^{r}{max(dp[i][0],dp[i][1])}+op?0:1 dp[r][0]=i=lrmax(dp[i][0],dp[i][1])+op?0:1
之后将所有l-r的集合合并到r上
最后统计答案的时候只需要将剩余的集合每个聚合取 m a x ( d p [ i ] [ 0 ] , d p [ i ] [ 1 ] ) max(dp[i][0],dp[i][1]) max(dp[i][0],dp[i][1])累加起来即可

代码

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <set>
#include <cmath>
#include <vector>
#include <map>
#include <cstring>
#include <queue>
#include <stack>

using namespace std;

typedef long long ll;
typedef double db;
typedef unsigned long long ull;
#define PB push_back
#define MP make_pair
#define Fi first
#define Se second

const int N=3e5+7,M=2e6+7;
struct node{
    int x,H,op;
}que[N];
int fa[N],h[N];
int n,m;
bool cmp(node a,node b){
    return a.H<b.H;
}
int cha(int x){
    if(fa[x]==x) return x;
    return fa[x]=cha(fa[x]);
}
int Max(int a,int b){return a > b?a:b;}
namespace sgt
{
    #define mid ((l+r)>>1)
    int maxx[N<<2];
    void up(int rt)
    {
        maxx[rt] = Max(maxx[rt<<1],maxx[rt<<1|1]);
    }
    void build(int rt,int l,int r)
    {
        if(l==r)
        {
            maxx[rt] = h[l];
            return;
        }
        build(rt<<1,l,mid);
        build(rt<<1|1,mid+1,r);
        up(rt);
    }
    int query_l(int rt,int l,int r,int x,int y,int v)
    {
        if(maxx[rt]<=v) return -1;
        if(l==r)
        {
            return l;
        }
        int ans = -1;
        if(y>mid) ans = query_l(rt<<1|1,mid+1,r,x,y,v);
        if(ans==-1&&x<=mid) ans = query_l(rt<<1,l,mid,x,y,v);
        return ans;
    }
    int query_r(int rt,int l,int r,int x,int y,int v)
    {
        if(maxx[rt]<=v) return -1;
        if(l==r) return l;
        int ans = -1;
        if(x<=mid) ans =query_r(rt<<1,l,mid,x,y,v);
        if(ans==-1&&mid<y) ans = query_r(rt<<1|1,mid+1,r,x,y,v);
        return ans;
    }
    #undef mid
}
int get(int x,int v,int flag)
{
    if(flag==0)
    {
        if(x==1) return -1;
        int xb = sgt::query_l(1,1,n,1,x-1,v);
        return xb;
    }
    else
    {
        int xb = sgt::query_r(1,1,n,x,n,v);
        return xb;
    }
}
int dp[N][3];
int main(){
    //freopen("d.txt","r",stdin);
    int t;
    int ti=0;
    scanf("%d",&t);
    while(t--){
        scanf("%d%d",&n,&m);
        memset(dp,0,sizeof(dp));
        for(int i=1;i<=n-1;i++){
            scanf("%d",&h[i]);
        }
        h[n]=1e9+7;
        sgt::build(1,1,n);
        for(int i=1;i<=n+3;i++) fa[i]=i;
        for(int i=1;i<=m;i++){
            scanf("%d%d%d",&que[i].x,&que[i].H,&que[i].op);
        }
        sort(que+1,que+1+m,cmp);
        for(int i=1;i<=m;i++){
            int now=que[i].x;
            int l=get(que[i].x,que[i].H,0);
            int r=get(que[i].x,que[i].H,1);
            if(l==-1) l=1;
            else l++;
            //cout<<l<<' '<<r<<' '<<que[i].H<<endl;
            int x=cha(l);
            int y=cha(r);
            //cout<<x<<' '<<y<<' '<<que
            if(x==y){
                int res1=0,res0=0;
                if(que[i].op==1){
                    res0=max(dp[y][1],dp[y][0]);
                    res1=dp[y][1]+1;
                }
                else {
                    res0=max(dp[y][1],dp[y][0])+1;
                    res1=dp[y][1];
                }
                dp[y][1]=res1;
                dp[y][0]=res0;
            }
            else {
                int res1=0,res0=0;
                while(x!=y){
                    if(que[i].op==1){
                        res0+=max(dp[x][1],dp[x][0]);
                        res1+=dp[x][1];
                    }
                    else {
                        res0+=max(dp[x][1],dp[x][0]);
                        res1+=dp[x][1];
                    }
                    fa[x]=cha(x+1);
                    x=cha(x);
                }
                if(que[i].op==1){
                    res0+=max(dp[y][1],dp[y][0]);
                    res1+=dp[y][1]+1;
                }
                else {
                    res0+=max(dp[y][1],dp[y][0])+1;
                    res1+=dp[y][1];
                }
                dp[y][1]=res1;
                dp[y][0]=res0;
            }
            //cout<<"-----"<<que[i].H<<' '<<que[i].op<<endl;
            //cout<<dp[y][1]<<' '<<dp[y][0]<<endl;
        }
        int x=cha(1);
        int y=cha(n);
        int ans=0;
        while(x!=y){
            ans+=max(dp[x][0],dp[x][1]);
            //ans+=dp[x][1];
            x=cha(x+1);
        }
        ans+=max(dp[y][1],dp[y][0]);
        //ans+=dp[y][1];
        printf("Case #%d: %d\n",++ti,ans);
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值