[BZOJ 2957]楼房重建
Link
题目描述
动态地维护一个以斜率为关键字的最长严格上升子序列。
解法
对于修改操作,只影响一个块,每次修改都暴力重构一下,计算出每个斜率并按块内斜率排序,最后存入一个vector中(其实并不需要排序,因为本身插入的时候就是按照“单调递增——小了就跳过,大了就插入并更新斜率最大值”)单次修改时间复杂度 \(O(\sqrt{n})\)
inline void modify(int x,int y){
double maxx=0;//斜率最大值
num[x]=y;//赋值
c[pos[x]].clear();//暴力重构
for(int i=(pos[x]-1)*s+1;i<=pos[x]*s&&i<=n;i++){
if(!num[i]) continue;//如果当前的位置没有楼房就跳过
double ret=((double)num[i]/(1.0*i));//计算斜率
if(maxx<ret) c[pos[x]].push_back(ret),maxx=ret;//插入数列,并更新max
}
/* for(vector<double>::iterator it=c[pos[x]].begin();it!=c[pos[x]].end();it++)
printf("%.3lf ",*it);
printf("\n");*/
}
对于查询操作,枚举每一个块,在块内二分上一个块的斜率最大值(即当前块内能看到的楼房的斜率最小值),累计上答案。单次查询 \(O(\sqrt{n})\)
inline int query_(int x,double val){
return c[x].end()-upper_bound(c[x].begin(),c[x].end(),val);//返回块内大于val的数的个数
}
inline int query(){
int ans=0;
double maxx=(double)num[st]/st;//st表示第一个的楼房的位置
for(int i=1;i<=pos[n];i++){
ans+=query_(i,maxx);//在块内二分
if(c[i].empty()) continue;//当前块没有楼房就跳过
double ret=(*(--c[i].end()));//当前块的最大值(因为块内是有序的,所以最后一个数就是当前块的最大值)
if(maxx<ret) maxx=ret;//更新最大值
}
return ans+1;//第一个楼房没算,所以要+1
}
时间复杂度 \(O(m\)\sqrt{n})\()\)
#include<stdio.h>
#include<math.h>
#include<vector>
#include<algorithm>
using namespace std;
#define N 100007
#define sqN 1007
template<class T>
inline void read(T &x){
x=0;char c=getchar();T flag=1;
while(c<'0'||c>'9'){if(c=='-')flag=-1;c=getchar();}
while(c>='0'&&c<='9'){x=(x<<1)+(x<<3)+c-48;c=getchar();}
x*=flag;
}
vector<double> c[sqN];
int s,n,m,pos[N],num[N],st;
inline int min(int x,int y){return x<y? x:y;}
inline int max(int x,int y){return x>y? x:y;}
inline void modify(int x,int y){
double maxx=0;num[x]=y;
c[pos[x]].clear();
for(int i=(pos[x]-1)*s+1;i<=pos[x]*s&&i<=n;i++){
if(!num[i]) continue;
double ret=((double)num[i]/(1.0*i));
if(maxx<ret) c[pos[x]].push_back(ret),maxx=ret;
}
/* for(vector<double>::iterator it=c[pos[x]].begin();it!=c[pos[x]].end();it++)
printf("%.3lf ",*it);
printf("\n");*/
}
inline int query_(int x,double val){
return c[x].end()-upper_bound(c[x].begin(),c[x].end(),val);
}
inline int query(){
int ans=0;
double maxx=(double)num[st]/st;
for(int i=1;i<=pos[n];i++){
ans+=query_(i,maxx);
if(c[i].empty()) continue;
double ret=(*(--c[i].end()));
if(maxx<ret) maxx=ret;
}
return ans+1;
}
int main(){
// freopen("data.in","r",stdin);
// freopen("mine.out","w",stdout);
read(n),read(m),s=(int)(sqrt(n)+0.5),st=n+1;
for(int i=1;i<=n;i++) pos[i]=(i-1)/s+1;
int x,y;
while(m--){
read(x);read(y);
st=min(st,x);modify(x,y);
printf("%d\n",query());
}
}
/*
5 7
4 6
3 2
1 19
5 11
2 17
1 1
1 1
*/