【日记】12.8

走远了吗. 提交于 2019-12-09 01:53:26

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\n",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\nTotal explored area: %.2f\n\n",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\nTotal explored area: %.2f\n\n",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\n",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\n",b[query(root[ca-1],root[cb],1,len,cb-ca+1)]);
    }
    return 0;
}

明日计划

  1. 扫描线三维:
  2. 主席树:P1801(对顶堆), P2633
  3. 可删堆:
  4. CDQ代替树状数组。
标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!