CF1557D Ezzat and Grid
前言
线段树好题一个。
解题思路
我们首先先要对数据进行离散化,这是一个常规套路。
正难则反,我们考虑 dp
。令 \(f_i\) 表示以 \(i\) 为结尾,最多可以选择多少行。显然,我们有转移方程:
\[f_i=\max\limits_{j<i \operatorname{and} g(i,j)} f_j+1
\]
其中 \(g(i,j)\) 为一个布尔函数,表示第 \(i,j\) 行是否可以相邻。
这显然是一个 \(O(n^3)\) 级别的算法,我们考虑优化一下,可以用线段树。
我们假设现在考虑已经求出对于 \(j\in [1,i]\) 中的所有 \(f_j\),我们线段树维护的是目前,区间所被覆盖的行中 \(f_i\) 值最大的那一个,这样我们在求解 \(f_{i+1}\)? 时,可以利用线段树上的信息了。
最后答案就是 \(n-\max\limits_{i=1}^n f_i\)。
求解方案的话,只要记录一下决策点即可。
时间复杂度 \(O(n\log n)\)。线段树帮助我们省去了枚举 \(j\) 和计算 \(g\) 函数的时间。
代码
注意有几处等于号一定要有,否则会错。
别问我怎么知道。
22次的提交记录告诉我的
原因的话,是因为可能因为 pushup
操作导致父节点的 \(maxx\)? 值已经正确,但是另外一个子节点无法更新到。
这话很抽象,最好在实践中理解。。。。。
//Don‘t act like a loser.
//This code is written by huayucaiji
//You can only use the code for studying or finding mistakes
//Or,you‘ll be punished by Sakyamuni!!!
#include<bits/stdc++.h>
using namespace std;
int read() {
char ch=getchar();
int f=1,x=0;
while(ch<‘0‘||ch>‘9‘) {
if(ch==‘-‘)
f=-1;
ch=getchar();
}
while(ch>=‘0‘&&ch<=‘9‘) {
x=x*10+ch-‘0‘;
ch=getchar();
}
return f*x;
}
const int MAXN=3e5+10;
int n,m,len,cnt;
struct segment {
int maxx,lazy;
}s[MAXN<<3];
struct sgm {
int id,l,r;
}sm[MAXN];
int num[MAXN<<1],f[MAXN],g[MAXN];
bool vis[MAXN];
vector<sgm> vec[MAXN];
void pushdown(int l,int r,int p) {
if(l==r) {
return ;
}
int mid=(l+r)>>1;
if(f[s[p].lazy]>=f[s[p<<1].maxx]) {
s[p<<1].maxx=s[p].lazy;
s[p<<1].lazy=s[p].lazy;
}
if(f[s[p].lazy]>=f[s[p<<1|1].maxx]) {
s[p<<1|1].maxx=s[p].lazy;
s[p<<1|1].lazy=s[p].lazy;
}
s[p].lazy=0;
}
segment pushup(segment lft,segment rgt) {
segment ret=s[0];
if(f[lft.maxx]>f[rgt.maxx]) {
ret.maxx=lft.maxx;
}
else {
ret.maxx=rgt.maxx;
}
return ret;
}
void modify(int l,int r,int p,int x,int y,int val) {
if(r<x||y<l) {
return ;
}
pushdown(l,r,p);
if(x<=l&&r<=y) {
if(f[val]>=f[s[p].maxx]) {
//here
s[p].maxx=val;
s[p].lazy=val;
}
return ;
}
int mid=(l+r)>>1;
modify(l,mid,p<<1,x,y,val);
modify(mid+1,r,p<<1|1,x,y,val);
s[p]=pushup(s[p<<1],s[p<<1|1]);
}
segment query(int l,int r,int p,int x,int y) {
if(r<x||y<l) {
return s[0];
}
pushdown(l,r,p);
if(x<=l&&r<=y) {
return s[p];
}
int mid=(l+r)>>1;
return pushup(query(l,mid,p<<1,x,y),query(mid+1,r,p<<1|1,x,y));
}
int get(int x) {
return lower_bound(num+1,num+len+1,x)-num;
}
signed main() {
cin>>n>>m;
for(int i=1;i<=m;i++) {
sm[i].id=read();num[++cnt]=sm[i].l=read();num[++cnt]=sm[i].r=read();
}
sort(num+1,num+cnt+1);
len=unique(num+1,num+cnt+1)-num-1;
for(int i=1;i<=m;i++) {
sm[i].l=get(sm[i].l);
sm[i].r=get(sm[i].r);
vec[sm[i].id].push_back(sm[i]);
}
int tp=0,maxx=0;
for(int i=1;i<=n;i++) {
if(i==6) {
int hhh;
hhh++;
}
segment ans=s[0];
int sz=vec[i].size();
for(int j=0;j<sz;j++) {
ans=pushup(ans,query(1,len,1,vec[i][j].l,vec[i][j].r));
}
f[i]=f[ans.maxx]+1;
g[i]=ans.maxx;
//cout<<f[i]<<" "<<g[i]<<endl;
if(f[i]>maxx) {
maxx=f[i];
tp=i;
}
for(int j=0;j<sz;j++) {
modify(1,len,1,vec[i][j].l,vec[i][j].r,i);
}
}
int ans=n;
while(tp) {
ans--;
vis[tp]=1;
tp=g[tp];
}
cout<<ans<<endl;
for(int i=1;i<=n;i++) {
if(!vis[i]) {
printf("%d ",i);
}
}
return 0;
}