今天刷了三个题,爽爆了
题意:
有一种输出n个数字最大值的数据结构,它的内部是m个小装置组成的,第i个小装置可以将l[i]到r[i]范围内的数字升序排列。小装置顺序固定,过程必须一个一个进行。有时减少一些小装置,这个数据结构仍能正常运作,请问最少有几个小装置。
Input
Output
首先是动态规划,dp[i]表示把最大值从1位置搞到第i个小装置结尾最少需要多少个小装置,这样的话,从小到大遍历所有装置,每次查询当前装置之前的装置区间和当前装置相交的装置,更新dp就可以了。
那么问题就来了,装置有m个,这样O(m^2)的算法绝壁TLE。
用线段树来维护区间最小dp值信息,每个点维护ll到rr范围内的dp最小是多少。没算完一个新的小装置只需把它的dp值插到树上就行了。
然后TLE了,这里有个小贪心,每次更新不需要更新区间信息,因为对每个区间,r点之前的信息对更新之后的装置dp没有贡献,因为要努力使最大值向右移,因此单点更新即可。虽然不知道是不是我的lazy标记写挫了。
代码如下:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<cmath>
using namespace std;
#define mxn 50010
#define mxm 500010
#define inf 0x3f3f3f3f
int n,m;
int dp[mxm];
struct node{
int ll,rr;
int minn;
};
class segment_tree{
private:
node nd[mxm<<2];
public:
void build(int l,int r,int id){
nd[id].ll=l;
nd[id].rr=r;
nd[id].minn=inf;
if(l==r) return;
int m=(l+r)>>1;
int ls=id<<1,rs=ls|1;
build(l,m,ls);
build(m+1,r,rs);
}
int find(int l,int r,int id){
if(nd[id].ll==l&&nd[id].rr==r) return nd[id].minn;
int m=(nd[id].ll+nd[id].rr)>>1;
int ls=id<<1,rs=ls|1;
if(r<=m) return find(l,r,ls);
else if(l>m) return find(l,r,rs);
else return min(find(l,m,ls),find(m+1,r,rs));
}
void insert(int loc,int x,int id){
nd[id].minn=min(nd[id].minn,x);
int m=(nd[id].ll+nd[id].rr)>>1;
int ls=id<<1,rs=ls|1;
if(nd[id].ll==nd[id].rr) return;
if(loc<=m) insert(loc,x,ls);
else insert(loc,x,rs);
}
}Tree;
int main(){
memset(dp,0x3f,sizeof(dp));
scanf("%d%d",&n,&m);
int a,b,ans=inf;
Tree.build(1,n,1);
for(int i=0;i<m;++i){
scanf("%d%d",&a,&b);
if(a==1) dp[i]=1;
else dp[i]=inf;
int tem=Tree.find(a,b,1);
dp[i]=min(dp[i],tem+1);
Tree.insert(b,dp[i],1);
if(b==n) ans=min(ans,dp[i]);
}
printf("%d\n",ans);
return 0;
}