题目大意:
就是给你很多行的
01
01
01串,长度是
1
e
9
1e9
1e9,每一行都有若干段的连续的
1
1
1,对于给定的串集合是
美
丽
的
美丽的
美丽的的条件是任意相邻两行的串至少有一个列是同时有
1
1
1的,问你至少删除多少行使得这些
01
01
01是美丽的?
n
,
m
∈
[
3
e
5
]
n
是
行
数
,
m
是
线
段
数
n,m\in[3e5]n是行数,m是线段数
n,m∈[3e5]n是行数,m是线段数
输入是
i
,
l
,
r
i,l,r
i,l,r意思是第
i
i
i行的
[
l
,
r
]
[l,r]
[l,r]的范围内是
1
1
1
解题思路:
- 这个题目有个比较麻烦的地方就是要输出方案
- 首先我们很明显的dp的思想就是 d p [ i ] 表 示 选 了 第 i 行 并 且 以 第 i 行 结 尾 能 构 成 最 大 的 dp[i]表示选了第i行并且以第i行结尾能构成最大的 dp[i]表示选了第i行并且以第i行结尾能构成最大的 01 串 集 合 01串集合 01串集合
- d p [ i ] = 1 + m a x { d p [ j ] } ∣ 如 果 第 j 行 和 i 行 有 同 列 为 1 dp[i] = 1+max\{dp[j]\}|如果第j行和i行有同列为1 dp[i]=1+max{dp[j]}∣如果第j行和i行有同列为1
- 但是这个非常不好维护,那么 d p 维 数 + 1 dp维数+1 dp维数+1进行进一步的细分
- d p [ i ] [ j ] dp[i][j] dp[i][j]表式前 i i i行并且在第 j j j列有1的最大集合
- 很明 d p [ i ] [ j ] = 1 + m a x k ∈ C i { d p [ i ] [ k ] } C i 是 i 行 里 面 有 1 的 列 dp[i][j] =1+max_{k\in C_i} \{dp[i][k]\} Ci是i行里面有1的列 dp[i][j]=1+maxk∈Ci{dp[i][k]}Ci是i行里面有1的列
- 实际上上面的dp方程可以写成
- d p [ i ] [ k ] = 1 + m a x { d p [ i − 1 ] [ k ] } ∣ k ∈ C i dp[i][k] =1+max \{dp[i-1][k]\}|_{k\in C_i} dp[i][k]=1+max{dp[i−1][k]}∣k∈Ci
- C i 是 i 行 里 面 有 1 的 列 C_i是i行里面有1的列 Ci是i行里面有1的列
- 那么这个dp是一段区间一段区间的进行更新的!!因为1是连续的那么就是线段树啦线段树维护的是当前行可以跟新的每一列的信息,要求区间加,然后维护区间最大值!!
- 因为要输出方案,那么我们在线段树里面保存最大值是被哪一行更新的就可以了
AC代码:
#include <bits/stdc++.h>
#define mid ((l + r) >> 1)
#define Lson rt << 1, l , mid
#define Rson rt << 1|1, mid + 1, r
#define ms(a,al) memset(a,al,sizeof(a))
#define log2(a) log(a)/log(2)
#define lowbit(x) ((-x) & x)
#define IOS std::ios::sync_with_stdio(0); cin.tie(0); cout.tie(0)
#define INF 0x3f3f3f3f
#define LLF 0x3f3f3f3f3f3f3f3f
#define f first
#define s second
#define endl '\n'
using namespace std;
const int N = 2e6 + 10, mod = 1e9 + 9;
const int maxn = 600010;
const long double eps = 1e-5;
const int EPS = 500 * 500;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int,int> PII;
typedef pair<ll,ll> PLL;
typedef pair<double,double> PDD;
template<typename T> void read(T &x) {
x = 0;char ch = getchar();ll f = 1;
while(!isdigit(ch)){if(ch == '-')f*=-1;ch=getchar();}
while(isdigit(ch)){x = x*10+ch-48;ch=getchar();}x*=f;
}
template<typename T, typename... Args> void read(T &first, Args& ... args) {
read(first);
read(args...);
}
struct Segtree {
PII tr[maxn << 2];
int lazy[maxn << 2];
void build(int rt, int l, int r) {
tr[rt] = {0,-1};//第一位dp。第二维更新的位置
lazy[rt] = 0;
if(l == r) return;
build(Lson);
build(Rson);
}
void pushdown(int rt) {
if(lazy[rt]) {
lazy[rt<<1] = lazy[rt<<1|1] = 1;
tr[rt<<1] = tr[rt<<1|1] = tr[rt];
lazy[rt] = 0;
}
}
void pushup(int rt) {
tr[rt] = max(tr[rt<<1],tr[rt<<1|1]);
}
PII quray(int rt, int l, int r, int posl, int posr) {
if(posl <= l && posr >= r) return tr[rt];
pushdown(rt);
PII res = {0,-1};
if(posl <= mid) res = max(quray(Lson,posl,posr),res);
if(posr > mid) res = max(quray(Rson,posl,posr),res);
return res;
}
void update(int rt, int l, int r, int posl, int posr, PII mx) {
if(posl <= l && posr >= r) {
tr[rt] = mx;
lazy[rt] = 1;
return;
}
pushdown(rt);
if(posl <= mid) update(Lson,posl,posr,mx);
if(posr > mid) update(Rson,posl,posr,mx);
pushup(rt);
}
}sgt;
int path[maxn];
vector<int> lis;
vector<PII> lin[maxn];
bool vis[maxn];
inline int getid(int x) {
return lower_bound(lis.begin(),lis.end(),x) - lis.begin() + 1;
}
int main() {
IOS;
int n, m;
read(n,m);
for(int i = 1; i <= m; ++ i) {
int idx, l, r;
read(idx,l,r);
lin[idx].push_back({l,r});
lis.push_back(l);
lis.push_back(r);
}
sort(lis.begin(),lis.end();//离散化
lis.erase(unique(lis.begin(),lis.end()),lis.end());
int len = lis.size();
sgt.build(1,1,len);
for(int i = 1; i <= n; ++ i)
for(int j = 0; j < lin[i].size(); ++ j) {
lin[i][j].first = getid(lin[i][j].first);
lin[i][j].second = getid(lin[i][j].second);
}
for(int i = 1; i <= n; ++ i) {
PII mx = {0,-1};
for(auto it : lin[i]) mx = max(mx,sgt.quray(1,1,len,it.first,it.second));
path[i] = mx.second;//保持dp更新路径
mx.first ++;
mx.second = i;//区间更新
for(auto it : lin[i]) sgt.update(1,1,len,it.first,it.second,mx);
}
PII mx = sgt.quray(1,1,len,1,len);
int cur = mx.second;
int siz = n;
while(cur != -1) {
vis[cur] = true;
siz --;
cur = path[cur];
}
cout << siz << "\n";
for(int i = 1; i <= n; ++ i)
if(!vis[i])
cout << i << " ";
return 0;
}