Loading

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);
}
posted @ 2021-08-12 16:02  MQFLLY  阅读(45)  评论(0编辑  收藏  举报