D. Ezzat and Grid (线段树维护dp)

D. Ezzat and Grid

题目传送门:D. Ezzat and Grid

题目大意:

就是给你很多行的01串,长度是1e9,每一行都有若干段的连续的1,使得给定的串集合美丽的条件是任意相邻两行的串至少有一个列是同时有1的,问你至少删除多少行使得这些01串是美丽的,并输出删除的方案?

思路:

比较好想到的就是维护dp[i]表示以第i行为结尾时构成的最大集合,dp[i]可以被第j行更新(第i行和第j行有同列为1),然后取一个max即可。由于每一行的1都是连续的,与是需要执行的是区间加和区间最值查询的操作,于是可以想到用线段树来加速这一dp过程。输出方案的话就记录一下当前这行被哪一行所更新的即可。

AC Code

#include<bits/stdc++.h>
using namespace std;
typedef pair<int,int>pii;
const int N=3e5+10;
vector<pii>g[N];    //存每一行的1的区间
int num[N*2],tot=0,cnt=0; 
int path[N],ans[N];
int find(int x)
{
    return lower_bound(num+1,num+1+cnt,x)-num;
}
struct node
{
    int l,r,lazy;
    pii w;
}tr[N<<3];
void pushup(int k)
{
    tr[k].w=max(tr[k<<1].w,tr[k<<1|1].w);
    return ;
}
void pushdown(int k)
{
    if(tr[k].lazy==0) return ;
    tr[k<<1].w=tr[k].w;
    tr[k<<1|1].w=tr[k].w;
    tr[k<<1].lazy=tr[k<<1|1].lazy=1;
    tr[k].lazy=0;
    return ;
}
void build(int k,int l,int r)
{
    tr[k].l=l,tr[k].r=r;
    tr[k].lazy=0,tr[k].w={0,-1};
    if(l==r) return  ;
    int mid=(l+r)/2;
    build(k<<1,l,mid);
    build(k<<1|1,mid+1,r);
}
pii query(int k,int l,int r)
{
    if(tr[k].l>=l&&tr[k].r<=r) return tr[k].w;
    pushdown(k);
    int mid=(tr[k].l+tr[k].r)/2;
    pii mx={0,-1};
    if(l<=mid) mx=query(k<<1,l,r);
    if(r>mid) mx=max(mx,query(k<<1|1,l,r));
    return mx;
}
void modify(int k,int l,int r,pii x)
{
    if(tr[k].l>=l&&tr[k].r<=r)
    {
        tr[k].lazy=1;
        tr[k].w=x;
        return ;
    }
    int mid=(tr[k].l+tr[k].r)/2;
    if(l<=mid) modify(k<<1,l,r,x);
    if(r>mid) modify(k<<1|1,l,r,x);
    pushup(k);
}
int main()
{
    int n,m;
    scanf("%d%d",&n,&m);
    int idx,l,r;
    for(int i=1;i<=m;i++)
    {
        scanf("%d%d%d",&idx,&l,&r);
        g[idx].push_back({l,r});
        num[++tot]=l,num[++tot]=r;
    }
    sort(num+1,num+1+tot);
    cnt=unique(num+1,num+1+tot)-num-1;
    for(int i=1;i<=n;i++)   //将每一行中的每一段都进行离散化
    {
        for(int j=0;j<g[i].size();j++)
        {
            g[i][j].first=find(g[i][j].first);
            g[i][j].second=find(g[i][j].second);
        }
    }
    build(1,1,cnt);
    for(int i=1;i<=n;i++)
    {
        pii maxn={0,-1};
        for(int j=0;j<g[i].size();j++) maxn=max(maxn,query(1,g[i][j].first,g[i][j].second));
        path[i]=maxn.second;
        maxn.first++;
        maxn.second=i;
        for(int j=0;j<g[i].size();j++) modify(1,g[i][j].first,g[i][j].second,maxn);
    }
    pii mx=query(1,1,cnt);
    printf("%d\n",n-mx.first);
    int now=mx.second;
    while(now!=-1)
    {
        ans[now]=1;
        now=path[now];
    }
    for(int i=1;i<=n;i++)
        if(ans[i]==0) printf("%d ",i);
    printf("\n");
    //system("pause");
    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值