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;
}