• 【日记】12.8


    12.8日记

    扫描线

    1. P5490:矩形面积并。

    思路:看了一天才勉强看懂。首先离散化,线段树上每个节点表示一段区间。每次修改矩形的扫描线时,可以证明一定可以将其拆分成logn个区间,所以复杂度是对的。cnt记录这个区间被覆盖了几次。len记录这个区间至少被覆盖了一次的长度。这样每次加面积就是(O(1))的。记得不用下推标记&&数组开大点。

    #include<bits/stdc++.h>
    using namespace std;
    #define mid (l+r)/2
    #define LL long long
    const int M=4e5+20;
    LL lsh[M*2];
    unordered_map<double,int> rev;
    struct Line{
        int l,r,h,d;
        Line(int a=0,int b=0,int c=0,int dd=0):l(a),r(b),h(c),d(dd){}
        bool operator<(const Line &x)const {
            return h<x.h;
        }
    };
    vector<Line> line; 
    struct Tree{
        int cnt;//被完全覆盖的次数
        LL len;//区间长度
        Tree(int a=0,double b=0):cnt(a),len(b){}
    }v[4*M];
    inline void pushup(int id,int l,int r){
        if (v[id].cnt)
            v[id].len=lsh[r+1]-lsh[l];
        else
            v[id].len=v[id*2].len+v[id*2+1].len;
    }
    void build(int id,int l,int r){
        v[id].cnt=v[id].len=0;
        if (l==r)
            return;
        build(id*2,l,mid);
        build(id*2+1,mid+1,r);
    }
    void operate(int id,int l,int r,int ql,int qr,int x){
        if (ql<=l&&r<=qr){
            v[id].cnt+=x;
            pushup(id,l,r);
            return;
        }
        if (ql<=mid)
            operate(id*2,l,mid,ql,qr,x);
        if (mid<qr)
            operate(id*2+1,mid+1,r,ql,qr,x);
        pushup(id,l,r);
    }
    int main(){
        int n;
        while(~scanf("%d",&n)){
            line.clear(),rev.clear();
            for(int i=1;i<=n;++i){
                int a,b,c,d;
                scanf("%d%d%d%d",&a,&b,&c,&d);
                line.push_back(Line(a,c,b,1)),line.push_back(Line(a,c,d,-1)),
                lsh[2*i-1]=a,lsh[2*i]=c;
            }
            sort(line.begin(),line.end());
            sort(lsh+1,lsh+2*n+1);
            int len=unique(lsh+1,lsh+2*n+1)-(lsh+1)-1;
            for(int i=1;i<=len+1;++i)
                rev[lsh[i]]=i;
            build(1,1,len);
            LL ans=0;
            for(int i=1;i<n*2;++i){
                operate(1,1,len,rev[line[i-1].l],rev[line[i-1].r]-1,line[i-1].d);
                ans+=v[1].len*(line[i].h-line[i-1].h);
            }
            printf("%lld
    ",ans);
        }
        return 0;
    }
    
    
    1. HDU1542:实数矩形面积并

    和上一题一模一样。

    #include<bits/stdc++.h>
    using namespace std;
    #define mid (l+r)/2
    const int M=4e5+20;
    double lsh[M*2];
    unordered_map<double,int> rev;
    struct Line{
        double l,r,h;
        int d;
        Line(double a=0,double b=0,double c=0,int dd=0):l(a),r(b),h(c),d(dd){}
        bool operator<(const Line &x)const {
            return h<x.h;
        }
    };
    vector<Line> line; 
    struct Tree{
        int cnt;//被完全覆盖的次数
        double len;//区间长度
        Tree(int a=0,double b=0):cnt(a),len(b){}
    }v[4*M];
    inline void pushup(int id,int l,int r){
        if (v[id].cnt)
            v[id].len=lsh[r+1]-lsh[l];
        else
            v[id].len=v[id*2].len+v[id*2+1].len;
    }
    void build(int id,int l,int r){
        v[id].cnt=v[id].len=0;
        if (l==r)
            return;
        build(id*2,l,mid);
        build(id*2+1,mid+1,r);
    }
    void operate(int id,int l,int r,int ql,int qr,int x){
        if (ql<=l&&r<=qr){
            v[id].cnt+=x;
            pushup(id,l,r);
            return;
        }
        if (ql<=mid)
            operate(id*2,l,mid,ql,qr,x);
        if (mid<qr)
            operate(id*2+1,mid+1,r,ql,qr,x);
        pushup(id,l,r);
    }
    int main(){
        int n,z=0;
        while(~scanf("%d",&n)&&n){
            ++z;
            line.clear(),rev.clear();
            for(int i=1;i<=n;++i){
                double a,b,c,d;
                scanf("%lf%lf%lf%lf",&a,&b,&c,&d);
                line.push_back(Line(a,c,b,1)),line.push_back(Line(a,c,d,-1)),
                lsh[2*i-1]=a,lsh[2*i]=c;
            }
            sort(line.begin(),line.end());
            sort(lsh+1,lsh+2*n+1);
            int len=unique(lsh+1,lsh+2*n+1)-(lsh+1)-1;
            for(int i=1;i<=len+1;++i)
                rev[lsh[i]]=i;
            build(1,1,len);
            double ans=0;
            for(int i=1;i<n*2;++i){
                operate(1,1,len,rev[line[i-1].l],rev[line[i-1].r]-1,line[i-1].d);
                ans+=v[1].len*(line[i].h-line[i-1].h);
            }
            printf("Test case #%d
    Total explored area: %.2f
    
    ",z,ans);
        }
        return 0;
    }
    
    
    1. HDU1255:至少覆盖了两次的矩形面积并

    思路:比较妙,主要还是要利用区间和的性质。len1表示当前区间至少被覆盖了1次的长度,len2表示当前区间至少被覆盖了2次的长度。那么len1和之前的处理一样,len2要分三种情况。cnt=2就是整个区间,cnt=1就是左右的len1和,cnt=0就是左右的len2和。注意要对叶子节点单独处理!!!前两道题就忘了要搞这个!!!

    #include<bits/stdc++.h>
    using namespace std;
    #define mid (l+r)/2
    const int M=4e5+20;
    double lsh[M*2];
    unordered_map<double,int> rev;
    struct Line{
        double l,r,h;
        int d;
        Line(double a=0,double b=0,double c=0,int dd=0):l(a),r(b),h(c),d(dd){}
        bool operator<(const Line &x)const {
            return h<x.h;
        }
    };
    vector<Line> line; 
    struct Tree{
        int cnt;//被完全覆盖的次数
        double len;//区间长度
        Tree(int a=0,double b=0):cnt(a),len(b){}
    }v[4*M];
    inline void pushup(int id,int l,int r){
        if (v[id].cnt)
            v[id].len=lsh[r+1]-lsh[l];
        else
            v[id].len=v[id*2].len+v[id*2+1].len;
    }
    void build(int id,int l,int r){
        v[id].cnt=v[id].len=0;
        if (l==r)
            return;
        build(id*2,l,mid);
        build(id*2+1,mid+1,r);
    }
    void operate(int id,int l,int r,int ql,int qr,int x){
        if (ql<=l&&r<=qr){
            v[id].cnt+=x;
            pushup(id,l,r);
            return;
        }
        if (ql<=mid)
            operate(id*2,l,mid,ql,qr,x);
        if (mid<qr)
            operate(id*2+1,mid+1,r,ql,qr,x);
        pushup(id,l,r);
    }
    int main(){
        int n,z=0;
        while(~scanf("%d",&n)&&n){
            ++z;
            line.clear(),rev.clear();
            for(int i=1;i<=n;++i){
                double a,b,c,d;
                scanf("%lf%lf%lf%lf",&a,&b,&c,&d);
                line.push_back(Line(a,c,b,1)),line.push_back(Line(a,c,d,-1)),
                lsh[2*i-1]=a,lsh[2*i]=c;
            }
            sort(line.begin(),line.end());
            sort(lsh+1,lsh+2*n+1);
            int len=unique(lsh+1,lsh+2*n+1)-(lsh+1)-1;
            for(int i=1;i<=len+1;++i)
                rev[lsh[i]]=i;
            build(1,1,len);
            double ans=0;
            for(int i=1;i<n*2;++i){
                operate(1,1,len,rev[line[i-1].l],rev[line[i-1].r]-1,line[i-1].d);
                ans+=v[1].len*(line[i].h-line[i-1].h);
            }
            printf("Test case #%d
    Total explored area: %.2f
    
    ",z,ans);
        }
        return 0;
    }
    
    

    主席树

    1. P3834:给一个序列,询问区间第k小。

    构造:主席树,叶子节点v[i]=j表示数i出现了j次,维护区间和,节点表示对应范围的数的个数。单点加减,单路查询。

    #include<bits/stdc++.h>
    using namespace std;
    #define mid ((l+r)/2)
    const int M=8e6+10,Mm=2e5+20;
    int cnt;
    int a[Mm],b[Mm],root[Mm],L[M],R[M],v[M];
    unordered_map<int,int> rev;
    int build(int l,int r){//建空树
        int rt=++cnt;
        v[rt]=0;
        if (l==r)
            return rt;
        L[rt]=build(l,mid);
        R[rt]=build(mid+1,r);
        return rt;
    }
    int update(int idp,int l,int r,int pos,int x){
        int rt=++cnt;
        L[rt]=L[idp],R[rt]=R[idp],v[rt]=v[idp]+x;
        if (l==r)
            return rt;
        if (pos<=mid)
            L[rt]=update(L[idp],l,mid,pos,x);
        else
            R[rt]=update(R[idp],mid+1,r,pos,x);
        return rt;
    }
    int query(int lid,int nid,int l,int r,int k){
        int Lnum=v[L[nid]]-v[L[lid]];//当前区间左子树数的个数
        if (l==r)
            return l;
        if (Lnum>=k)
            return query(L[lid],L[nid],l,mid,k);
        else
            return query(R[lid],R[nid],mid+1,r,k-Lnum);
    }
    int main(){
        int n,m;
        scanf("%d%d",&n,&m);
        for(int i=1;i<=n;++i)
            scanf("%d",&a[i]),b[i]=a[i];
        sort(b+1,b+n+1);
        int len=unique(b+1,b+n+1)-(b+1);
        for(int i=1;i<=len;++i)
            rev[b[i]]=i;
        root[0]=build(1,len);
        for(int i=1;i<=n;++i)
            root[i]=update(root[i-1],1,len,rev[a[i]],1);
        for(int i=1;i<=m;++i){
            int c1,c2,c3;
            scanf("%d%d%d",&c1,&c2,&c3);
            printf("%d
    ",b[query(root[c1-1],root[c2],1,len,c3)]);
        }
        return 0;
    }
    
    
    1. P3567:给定一序列,询问某一区间是否有出现>区间长度一半次的数,有则输出该数,没有则返回0。

    构造方式与上题相同,查询函数有所变化,这次比较的是固定长度len。

    #include<bits/stdc++.h>
    using namespace std;
    #define mid (l+r)/2
    const int M=1.6e7+10,Mm=5e5+20;
    int cnt,v[M],L[M],R[M],root[Mm],a[Mm],b[Mm];
    int build(int l,int r){
        int rt=++cnt;
        v[rt]=0;
        if (l==r)
            return rt;
        build(l,mid),build(mid+1,r);
        return rt;
    }
    int operate(int idp,int l,int r,int pos,int x){
        int rt=++cnt;
        L[rt]=L[idp],R[rt]=R[idp],v[rt]=v[idp]+x;
        if (l==r)
        	return rt;
        if (pos<=mid)
            L[rt]=operate(L[idp],l,mid,pos,x);
        else
            R[rt]=operate(R[idp],mid+1,r,pos,x);
        return rt;
    }
    int query(int lid,int nid,int l,int r,int len){
        if (l==r)
            return l;
        if (v[L[nid]]-v[L[lid]]>len/2)
            return query(L[lid],L[nid],l,mid,len);
        if (v[R[nid]]-v[R[lid]]>len/2)
            return query(R[lid],R[nid],mid+1,r,len);
        return 0;
    }
    unordered_map<int,int> rev;
    int main(){
        int n,m;
        scanf("%d%d",&n,&m);
        for(int i=1;i<=n;++i)
            scanf("%d",&a[i]),b[i]=a[i];
        sort(b+1,b+n+1);
        int len=unique(b+1,b+n+1)-(b+1);
        for(int i=1;i<=len;++i)
            rev[b[i]]=i;
        root[0]=build(1,len);
        for(int i=1;i<=n;++i)
            root[i]=operate(root[i-1],1,len,rev[a[i]],1);
        for(int i=1;i<=m;++i){
            int ca,cb;
            scanf("%d%d",&ca,&cb);
            printf("%d
    ",b[query(root[ca-1],root[cb],1,len,cb-ca+1)]);
        }
        return 0;
    }
    
    

    明日计划

    1. 扫描线三维:
    2. 主席树:P1801(对顶堆), P2633
    3. 可删堆:
    4. CDQ代替树状数组。
  • 相关阅读:
    2019 Multi-University Training Contest 1
    2019江西省省赛
    2019牛客暑期多校训练营 第二场
    母函数
    树形DP
    蓝桥杯-标题:打印图形
    蓝桥杯-标题:史丰收速算
    蓝桥杯-标题:切面条
    蓝桥杯-标题:李白打酒
    蓝桥杯-标题:啤酒和饮料
  • 原文地址:https://www.cnblogs.com/diorvh/p/12008847.html
Copyright © 2020-2023  润新知