题解 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\) 的最小值。

code:

#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;
}

线段树部分就是一个模板,还是十分容易写的。

AC

欢迎交流讨论,请点个赞哦~

posted @ 2021-07-17 19:52  Maplisky  阅读(44)  评论(0编辑  收藏  举报