题解 UVA1322 优化最大值电路 Minimizing Maximizer
作者:互联网
算法分析:线段树优化 dp
先提醒一下坑点:
- 题中 Sorter 的顺序不可改变。
一些约定:
- 记第 \(k\) 个 Sorter 的左右端点为 \(l_k\),\(r_k\),长度为 \(len_k\)。
首先分析 dp。我们关心的是能对目标序列全部排序的最少 Sorter 个数,但与第几个无关,因此我们设计状态 \(dp_i\) 表示将目标序列前 \(i\) 个数进行排序的最少 Sorter 个数(即右端点为 \(i\))。对于第 \(k\) 个 Sorter,有:
\[dp_{r_k}=\min(dp_j+1),j\in (l_k,r_k) \]初始值为 \(dp_1=0,dp_i=inf,(i\ne1)\),边界为 \(dp_n\)。
此时,对于每一个 Sorter,都要进行 \(r_k-l_k+1\) 次检查,时间复杂度为 \(\mathcal{O}(m\times \sum\limits_{k=1}^nlen_k)\)。显然超时。
考虑优化转移。
在暴力转移时,我们是从一段连续区间内取一个最小值。需要区间修改,区间查询最小值的操作。显然,线段树可以完成这个任务。于是我们用线段树代替循环进行查找和转移。我们可以直接查询 \(l_k\to r_k\) 这一段 \(dp\) 的最小值 \(x\),然后再把 \(x+1\) 作为新的 \(dp\) 值储存到对应的区间上。最后答案就是 \(1\to n\) 的最小值。
具体见代码:
#include<cstdio>
#include<ctype.h>
#include<algorithm>
#include<iostream>
#include<cstring>
#define reg register
#define F(i,a,b) for(reg int i=(a);i<=(b);++i)
using namespace std;
inline int read();
const int N=5e5+5,inf=1e9;
int n,m,dp[N],ans;
struct P {
int l,r;
} a[N];
namespace tree {
#define ls (x<<1)
#define rs (x<<1|1)
const int P=5e4+10;
struct P {
int l,r,val,lz;
} f[P<<2];
void build(int x,int l,int r) {
f[x]= {l,r,inf,inf};
if(l==r) {
f[x].val=inf;//赋极大值
return;
}
int mid=l+r>>1;
build(ls,l,mid);
build(rs,mid+1,r);
}
inline void push_down(int x) {
if(f[x].lz==inf)return;
//区间修改,取最小值
f[ls].val=min(f[ls].val,f[x].lz);
f[ls].lz=min(f[ls].lz,f[x].lz);
f[rs].val=min(f[rs].val,f[x].lz);
f[rs].lz=min(f[rs].lz,f[x].lz);
f[x].lz=inf;
}
int query(int x,int l,int r) {//查找最小值
if(l<=f[x].l and f[x].r<=r)return f[x].val;
push_down(x);
int mid=f[x].l+f[x].r>>1,ans=m;
if(l<=mid)ans=min(ans,query(ls,l,r));
if(r>mid)ans=min(ans,query(rs,l,r));
return ans;
}
void modify(int x,int l,int r,int val) {
if(l<=f[x].l and f[x].r<=r) {
f[x].val=min(f[x].val,val);
f[x].lz=min(f[x].lz,val);
return;
}
push_down(x);
int mid=f[x].l+f[x].r>>1;
if(l<=mid)modify(ls,l,r,val);
if(r>mid)modify(rs,l,r,val);
f[x].val=min(f[ls].val,f[rs].val);
}
}
int main() {
int T=read();
while(T--) {
n=read(),m=read();
tree::build(1,1,n);//建树并赋极大值
tree::modify(1,1,1,0);//赋值 dp[1]=0
F(i,1,m) {
a[i].l=read(),a[i].r=read();
int x=tree::query(1,a[i].l,a[i].r);//查找最小值
tree::modify(1,a[i].l,a[i].r,x+1);//转移
}
ans=tree::query(1,n,n);//最终答案
printf("%d\n",ans);
if(T)putchar('\n');
}
return 0;
}
inline int read() {
reg int x=0;
reg char c=getchar();
while(!isdigit(c))c=getchar();
while(isdigit(c))x=(x<<3)+(x<<1)+(c^48),c=getchar();
return x;
}
线段树部分就是一个模板,还是十分容易写的。
欢迎交流讨论,请点个赞哦~
标签:Maximizer,val,rs,int,题解,min,Minimizing,lz,dp 来源: https://www.cnblogs.com/lbh2021/p/15024700.html