Codeforces Round #737 (Div. 2) D. Ezzat and Grid DP+线段树优化
Codeforces Round #737 (Div. 2) D. Ezzat and Grid DP+线段树优化
题意
给定\(n\)行\(m\)列的01矩阵,删去若干行,使得结果矩阵满足任意相邻的两行存在某一列都是1
要求输出删去的方案
\[1 \leq n,m\leq 3\times 10^5\\
1 \leq i\leq n,1\leq l \leq r \leq 10^9
\]
分析
比较明显的可以列出dp方程 \(dp_i\)表示以\(i\)为结尾的行的最多行数
\(dp[i] =1 + max\{dp[j]\},ij有共同1\)
这样难点就成为了难以判断\(ij\)是否有共同的1
于是可以考虑
\[dp[i][j] = 1 + max\{dp[i-1][j]\} ,j \in C_i
\]
这样实际上\(dp\)的含义就变掉了,相当于维护了前\(i\)行的max
\(C_i\)表示第\(i\)行有1的列
区间个数是\(m\)级别,因此\(dp\)的过程最多进行\(O(m)\)次区间询问和更新
这题的另一个难点是方案,这是比较套路的,可以在线段树上维护更新来的行数和path路径用于回溯
代码
#include<bits/stdc++.h>
#define pii pair<ll,ll>
#define fi first
#define se second
#define equals(a,b) (fabs(a-b) < 1e-8)
using namespace std;
typedef long long ll;
inline ll rd(){
ll x = 0;
char ch = getchar();
while(ch < '0' || ch > '9'){
ch = getchar();
}
while(ch >= '0' && ch <= '9'){
x = x * 10 + ch - '0';
ch = getchar();
}
return x;
}
struct SegmentTree{
vector<pii> v;
vector<int> tag;
SegmentTree(int n):v((n + 1) << 2),tag((n + 1 )<< 2){}
inline void push_up(int i){
v[i] = max(v[i << 1],v[i << 1|1]);
}
inline void update(int i,pii val){
tag[i] = 1;
v[i] = val;
}
inline void push(int i){
if(tag[i]) {
update(i << 1,v[i]);
update(i << 1|1,v[i]);
tag[i] = 0;
}
}
void build(int i,int l,int r){
v[i] = make_pair(0,-1);
if(l == r)return;
int mid = l + r >> 1;
build(i << 1,l,mid);
build(i << 1|1,mid + 1,r);
push_up(i);
}
pii query(int i,int l,int r,int L,int R){
if(l > R || r < L) return make_pair(0,-1);
if(l >= L && r <= R) return v[i];
push(i);
int mid = l + r >> 1;
return max(query(i << 1,l,mid,L,R),query(i << 1|1,mid + 1,r,L,R));
}
void update(int i,int l,int r,int L,int R,pii val){
if(l > R || r < L) return;
if(l >= L && r <= R) return update(i,val);
int mid = l + r >> 1;
update(i << 1,l,mid,L,R,val);
update(i << 1|1,mid + 1,r,L,R,val);
push_up(i);
}
};
inline int get(int x,vector<int>&v){
return lower_bound(v.begin(),v.end(),x) - v.begin() + 1;
}
int main(){
int n = rd();
int m = rd();
vector<int> vis(n + 1);
vector<vector<pii>> lin(n + 1);
vector<int> vvv;
vector<int> path(n + 1);
for(int i = 1;i <= m;i++){
int R = rd();
int l = rd();
int r = rd();
lin[R].push_back(make_pair(l,r));
vvv.push_back(l);
vvv.push_back(r);
}
sort(vvv.begin(),vvv.end());
vvv.erase(unique(vvv.begin(),vvv.end()),vvv.end());
int N = vvv.size();
SegmentTree seg(N);
seg.build(1,1,N);
for(int i = 1;i <= n;i++){
for(auto &x:lin[i]){
x.fi = get(x.fi,vvv);
x.se = get(x.se,vvv);
}
}
for(int i = 1;i <= n;i++){
pii mx = make_pair(0,-1);
for(auto it:lin[i]) mx = max(mx,seg.query(1,1,N,it.fi,it.se));
path[i] = mx.se;
mx.fi++;
mx.se = i;
for(auto it:lin[i]) seg.update(1,1,N,it.fi,it.se,mx);
}
pii mx = seg.query(1,1,N,1,N);
int cur = mx.se;
int cnt = n;
while(cur != -1){
vis[cur] = 1;
cnt--;
cur = path[cur];
}
printf("%d\n",cnt);
for(int i = 1;i <= n;i++)
if(!vis[i]) printf("%d ",i);
}