zoukankan      html  css  js  c++  java
  • BZOJ2957楼房重建

    传送门  https://www.luogu.org/problemnew/show/P4198

    蒟蒻渣渣禹看到这个题时十分不想写线段树emmmm

    于是乎。。。。分块!!!

    分块。思路就是:如果一个楼房能被看见,当且仅当它的房顶到(0,0)连线的斜率 比前面的都大。我们暴力维护每个块内斜率的最长上升序列,然后查询的时候二分找到块内第一个能看到的,那么它之后的所有最长上升序列里的元素就都能被看到,再去搜下一个块就好了。

    下面我来讲下具体过程,我们要求的个数,就是斜率最长上升子序列,大家思考一下。。。显然成立。

    我们设a,b以及他们的高度为h[a],h[b],如果a不会被b挡住,那么我们可以推出这样一个式子:h[a]/a >= h[b]/b,交叉相乘之后可得:h[a]b >= h[b]a。

    对于原点的话我们只要判断h[a]是否大于0就行了。

    修改:然后我们暴力更新块内元素信息,每次清零,重新维护块内最长上升序列长度。

    查询:我们设last为上一个满足条件的块的末尾节点,那么从第一块开始到最后一块,在块内进行二分查找出与last相比刚满足条件的点,那么,从这个点一直到末尾均为合法序列,那么ans=这段序列的长度qwq,然后更新last为本块的末尾。

    我们在暴力搞定每个块的递增数列,把这些斜率从小到大塞到一个栈里边(时间复杂度O(n/b),b为块的大小)。

    然后查找时从头开始,在每个块对应的栈中二分查找第一个斜率比前一个大的位置,这个位置和栈里面后边的位置都能被看到(时间复杂度O(blog(n/b)))。

    总时间复杂度为O(n(n/b + blog(n/b)))≈O(n(n/b + blogn))。

    这样一来b=√(n/logn)/2有奇效。想知道为什么么?

    通过我巨水无比的计算,以及一次次试错试出的,不然会tle,除非你开o2,强烈建议用快读。

    不用o2优化代码如下qwq

    #include<cmath>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #define N 100005
    #define LL long long
    using namespace std;;
    int n,m,b,h[N],p[N];
    struct stack {
        int s[N],top;
    }t[330];
    bool check(LL a,LL b) {
        if(!b)
            return h[a] > 0;
        return (LL)(h[a] * b) > (LL)(h[b] * a);
    }
    inline int read()
    {
        int w=0;
        char ch;
        while(ch<'0'||ch>'9')
        {
            ch=getchar();
        }
        while(ch>='0'&&ch<='9')
        {
            w=(w<<3)+(w<<1)+ch-'0';
            ch=getchar();
        }
        return w;
    }
    int main() {
        n=read();
        m=read();
        b = sqrt(n * log(n) / 2);
        for(register int i = 1;i <= n;i++) 
            p[i] = (i - 1) / b + 1;
        while(m--) {
            int x,ans = 0;
            x=read();
            h[x]=read();
            register int l = (p[x] - 1) * b + 1,r = min(n,p[x] * b);
            t[p[x]].top = 0;
            for(register int i = l;i <= r;i++)
                if(check(i,t[p[x]].s[t[p[x]].top]))
                    t[p[x]].s[++t[p[x]].top] = i;
            for(register int i = 1,last = 0;i <= p[n];i++) {
                register int q = 0;
                l = 1,r = t[i].top;
                while(l <= r) {
                    register int mid = (l + r) >> 1;
                    if(check(t[i].s[mid],last)){ 
                        q = mid;
                        r = mid - 1;
                    }
                    else l = mid + 1;
                }
                if(q){
                    last = t[i].s[t[i].top];
                    ans += t[i].top - q + 1;
                }
            }
            printf("%d
    ",ans);
        }
    }
    

      

  • 相关阅读:
    (转载)Android content provider基础与使用
    如何解决Android的SDK与ADT不匹配问题
    Android 中断线程的处理
    用AsyncTask 来实现下载图片在android开发中
    开源自己的一个小android项目(美女撕衣服游戏)
    实现在Android开发中的Splash Screen开场屏的效果
    支持在安卓中UI(View)的刷新功能
    android从资源文件中读取文件流显示
    后缀数组 模板题 hdu1403(最长公共(连续)子串)
    Codeforces Round #383 (Div. 1) C(二分图)
  • 原文地址:https://www.cnblogs.com/excellent-zzy/p/10667424.html
Copyright © 2011-2022 走看看