思路:分块
提交:2次(第一次的求解有问题)
题解:
设块长为$T$,我们开$N/T$个单调栈,维护每一块的上升斜率。
修改时暴力重构整个块,$O(T)$
求解时记录一个最大斜率$lst$,然后块内二分,求出能看见几个,同时更新$lst$
时间复杂度$O(N*(T+frac{N}{T}*log_2T)$,也不知道怎么算最小值,瞎猜$T=sqrt{N*log_2N}$(其实当时算了一下,现在发现算错了,就当是猜的吧$qwq$),后来试了试,定块长$1000$也可以。
#include<cstdio> #include<iostream> #include<cmath> using namespace std; #define R register int #define ull unsigned long long #define ll long long #define pause (for(R i=1;i<=10000000000;++i)) #define In freopen("NOIPAK++.in","r",stdin) #define Out freopen("out.out","w",stdout) namespace Fread { static char B[1<<15],*S=B,*D=B; #ifndef JACK #define getchar() (S==D&&(D=(S=B)+fread(B,1,1<<15,stdin),S==D)?EOF:*S++) #endif inline int g() { R ret=0,fix=1; register char ch; while(!isdigit(ch=getchar())) fix=ch=='-'?-1:fix; if(ch==EOF) return EOF; do ret=ret*10+(ch^48); while(isdigit(ch=getchar())); return ret*fix; } inline bool isempty(const char& ch) {return (ch<=36||ch>=127);} inline void gs(char* s) { register char ch; while(isempty(ch=getchar())); do *s++=ch; while(!isempty(ch=getchar())); } } using Fread::g; using Fread::gs; namespace Luitaryi { const int N=100010; int n,m,T; int pos[N],l[1300],r[1300]; double a[N]; struct STK { double stk[1300]; int top; inline int calc(double x) { R l=1,r=top+1; while(l<r) { R md=l+r>>1; if(stk[md]<=x) l=md+1; else r=md; } return top+1-l; } }s[1300]; inline void main() { n=g(),m=g(); T=sqrt(n*log2(n)); for(R i=1;i<=n;++i) pos[i]=(i-1)/T+1; for(R i=1,lim=pos[n];i<=lim;++i) l[i]=(i-1)*T+1; for(R i=1,lim=pos[n];i<lim;++i) r[i]=i*T; r[pos[n]]=min(pos[n]*T,n); while(m--) { R ans=0; R x=g(),y=g(); R p=pos[x]; a[x]=1.0*y/x; s[p].top=0; for(R i=l[p],lim=r[p];i<=lim;++i) s[p].stk[s[p].top]<a[i]?s[p].stk[++s[p].top]=a[i]:0; register double lst=0.0; for(R i=1;i<=pos[n];++i) ans+=s[i].calc(lst),lst=max(lst,s[i].stk[s[i].top]); printf("%d ",ans); } } } signed main() { Luitaryi::main(); }
线段树的先咕着$QwQ$
2019.07.20