题意:数列长度为n,m次操作(n<=50000,m<=500000),每次操作将区间[si,ti]从小到大排序,求至少使用几次操作使数列的最后一个数与经过所有操作后相等;
思路:选取最少的操作得到最优解,一般采用dp;
假设原数列的第1个数为最大值,dp[j]表示最大值移动到第j个位置需要至少的操作数;
初始令dp[1]=0,dp[j]=inf(j>1);
对于每个i,有dp[ti]=min(dp[ti],min(dp[j](si<=j<=ti))+1);
若复杂度为O(nm)的话,会超时,所以求最小值是采用线段树优化(另开一个数组);
线段树单点更新,区间求极值;
#include<cstdio> #include<cstring> #include<algorithm> using namespace std; #define inf 0x3f3f3f3f int dp[5000100]; int num[5000100]; int n,m; int s[5000100],t[5000100]; void build(int l,int r,int pos){ num[pos]=dp[pos]=inf; if(l==r){ return; } int mid=(l+r)/2; build(l,mid,pos*2); build(mid+1,r,pos*2+1); } void update(int p,int val,int l,int r,int pos){ if(dp[pos]>val) dp[pos]=val; if(l!=r){ int mid=(l+r)/2; if(p<=mid) update(p,val,l,mid,pos*2); else if(mid<p) update(p,val,mid+1,r,pos*2+1); } } int query(int L,int R,int l,int r,int pos){ if(L<=l&&r<=R){ return dp[pos]; } int mid=(l+r)/2; if(R<=mid) return query(L,R,l,mid,pos*2); else if(mid<L) return query(L,R,mid+1,r,pos*2+1); else{ int t1,t2; t1=query(L,R,l,mid,pos*2); t2=query(L,R,mid+1,r,pos*2+1); return min(t1,t2); } } int main(){ int i,j,k; while(scanf("%d%d",&n,&m)!=EOF){ build(1,n,1); for(i=0;i<m;i++){ scanf("%d%d",&s[i],&t[i]); } num[1]=0; update(1,0,1,n,1); for(i=0;i<m;i++){ int v=min(num[t[i]],query(s[i],t[i]+1,1,n,1)+1); num[t[i]]=v; update(t[i],v,1,n,1); } printf("%d\n",num[n]); } return 0; }
时间: 2024-12-29 23:23:55