题面传送门
大概可以算线段树单侧递归的板子。
这个东西看上去没法修改
我们考虑合并的时候怎么合并。
我们要维护每个区间的最大值和该区间的长度,那么答案显然是第一个区间的长度。
然后对于每个区间,它的左区间的答案肯定能全部选入,那么考虑右区间。
右区间选入的值肯定不能小于左区间的最大值。
我们考虑右区间的左区间的最大值,如果右区间的左区间的最大值小于左区间最大值,那么右区间的左区间显然是没有用的,我们直接考虑递归找到右区间答案。
反之,那么右区间的答案显然全部有用,那么我们递归找左区间再加上右区间答案即可。
时间复杂度\(O(nlog^2n)\)
code:
#include<bits/stdc++.h> #define I inline #define max(a,b) ((a)>(b)?(a):(b)) #define min(a,b) ((a)<(b)?(a):(b)) #define abs(x) ((x)>0?(x):-(x)) #define re register #define ll long long #define db double #define N 200000 #define M 30 #define mod 1000000007 #define eps (1e-7) #define U unsigned int #define it iterator #define Gc() getchar() #define Me(x,y) memset(x,y,sizeof(x)) using namespace std; int n,m,x,y,z,F[N+5<<2];db Maxn[N+5<<2]; I int Query(db x,int l,int r,int now){ if(l==r) return Maxn[now]>x;int m=l+r>>1;return Maxn[now<<1]<=x?Query(x,m+1,r,now<<1|1):F[now]-F[now<<1]+Query(x,l,m,now<<1); } I void get(int x,db y,int l=1,int r=n,int now=1){ if(l==r) return (void)(Maxn[now]=y,F[now]=1);int m=l+r>>1;x<=m?get(x,y,l,m,now<<1):get(x,y,m+1,r,now<<1|1); Maxn[now]=max(Maxn[now<<1],Maxn[now<<1|1]);F[now]=F[now<<1]+Query(Maxn[now<<1],m+1,r,now<<1|1); } int main(){ freopen("1.in","r",stdin); re int i;scanf("%d%d",&n,&m);while(m--) scanf("%d%d",&x,&y),get(x,y*1.0/x),printf("%d\n",F[1]); }