2019.03.01【BZOJ2957】【洛谷P4198】楼房重建(线段树维护单调栈)

版权声明:转载请声明出处,谢谢配合。 https://blog.csdn.net/zxyoi_dreamer/article/details/88067207

BZOJ传送门

洛谷传送门


解析:

显然这道题没有贪心没有 D P DP ,什么都没有,就是一个单调栈。

对于区间问题,我们考虑用线段树来解决。

我们要合并两个区间的时候,其实就是要计算右儿子在左儿子最大值开头的条件下,能够拥有的最长单调栈长度。

对于一个点显然直接判断一下就行了。

对于一个区间,我们判断一下左儿子最大值与当前值的大小来决定是去左边找还是右边找。

递归就行了。

复杂度 O ( n log 2 n ) O(n\log^2 n) ,常数较小。


代码:

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define re register
#define gc get_char
#define cs const

namespace IO{
	inline char get_char(){
		static cs int Rlen=1<<20|1;
		static char buf[Rlen],*p1,*p2;
		return (p1==p2)&&(p2=(p1=buf)+fread(buf,1,Rlen,stdin),p1==p2)?EOF:*p1++;
	}
	
	inline int getint(){
		re char c;
		while(!isdigit(c=gc()));re int num=c^48;
		while(isdigit(c=gc()))num=(num+(num<<2)<<1)+(c^48);
		return num;
	}
}
using namespace IO;

cs int N=1e5+5;
int n,m;
double mx[N<<2];
int cnt[N<<2];

inline int count(int k,int l,int r,cs double &val){
	if(l==r)return mx[k]>val;
	int mid=(l+r)>>1;
	if(mx[k<<1]<=val)return count(k<<1|1,mid+1,r,val);
	else return cnt[k]-cnt[k<<1]+count(k<<1,l,mid,val);
}

inline void update(int k,int l,int r,cs int &pos,cs double &val){
	if(l==r){
		mx[k]=val;
		cnt[k]=1;
		return ;
	}
	int mid=(l+r)>>1;
	if(pos<=mid)update(k<<1,l,mid,pos,val);
	else update(k<<1|1,mid+1,r,pos,val);
	mx[k]=max(mx[k<<1],mx[k<<1|1]);
	cnt[k]=cnt[k<<1]+count(k<<1|1,mid+1,r,mx[k<<1]);
}

signed main(){
	n=getint(),m=getint();
	while(m--){
		int pos=getint();
		double k=(double)getint()/pos;
		update(1,1,n,pos,k);
		cout<<cnt[1]<<"\n";
	}
	return 0;
}

猜你喜欢

转载自blog.csdn.net/zxyoi_dreamer/article/details/88067207