bzoj2957: 楼房重建

来源:互联网 发布:java深入看什么书好 编辑:程序博客网 时间:2024/06/08 00:35

链接

  http://www.lydsy.com/JudgeOnline/problem.php?id=2957

题解

  这题卡eps还我1A!
  eps可能要用1e10才能过
  这题思路比较绕,首先看下怎样统计答案,只要扫一遍,如果一个数aia1 ai中的最大数,那么就把它计计入答案。
  首先考虑线段树,每个点提前预处理出当前区间的递增序列,查询就直接提取出log2N个区间,然后来合并。合并具体就是每做完一个区间就那这个区间的最大值去切后面的递增序列,可以来个二分。
  但是修改可能不太好处理,我们必须暴力重构从根节点到叶子节点上所有的区间,目前我没想到比较好的解决方案,貌似复杂度是O(N),常数还很大。
  容易发现,修改的复杂度远远超过查询的复杂度。这个时候考虑用分块来平衡这两个操作。
  分个块,每个块内维护递增序列,修改可以每次只暴力重构这个块,查询就直接扫描一下所有的块,搞搞就过了。复杂度O(NNlog2N)
  这个复杂度确实很高,但是跑的飞快,很重要的原因是因为你每做完一个块就会更新最值,下一次二分之前,如果发现当前最值已经大于了块的最值,就直接跳过,所以那个log应该是比较虚的。

代码

//分块+二分 #include <cstdio>#include <algorithm>#include <vector>#define maxn 100010#define eps 1e-10using namespace std;int N, M, size, ans;double k[maxn];vector<double> v[maxn];inline int read(int x=0){    char c=getchar();    while(c<48 or c>57)c=getchar();    while(c>=48 and c<=57)x=(x<<1)+(x<<3)+c-48, c=getchar();    return x;}inline void rebuild(int block){    double m=0;    int i, l=block<<size, r=(block+1<<size)-1;    v[block].clear();    for(i=l;i<=r;i++)if(k[i]>m)m=k[i],v[block].push_back(k[i]);}inline int find(int block, double low){    int l=0, r=v[block].size()-1, mid;    for(;l<r;)    {        mid=(l+r)>>1;        if(v[block][mid]>low)r=mid; else l=mid+1;    }    return v[block].size()-1-r+1;}void calc(){    int i; double m=-1;    ans=0;    for(i=0;i<=N>>size;i++)    {        if(v[i].size()==0)continue;        if(v[i][v[i].size()-1]<m+eps)continue;        ans+=find(i,m);        m=v[i][v[i].size()-1];    }}void init(){    int i, x;    N=read(), M=read();    for(size=1;(1<<size)*(1<<size)<N;size++);}void chg(int pos, double t){    int i;    k[pos]=t;    rebuild(pos>>size);}int main(){    int x, y, i;    init();    for(i=1;i<=M;i++)    {        x=read(), y=read();        chg(x,double(y)/x);        calc();        printf("%d\n",ans);    }    return 0;}
0 0
原创粉丝点击