zoukankan      html  css  js  c++  java
  • 考古研究——四十五度翻转+时光倒流+线段树二分

    3.考古研究

    (geologic.pas/c/cpp)

     

    【问题描述】

      很久很久以前,有一个叫NOIP的高级文明十分繁荣。但是由于火山喷发,这个高级文明最终还是毁灭了。NOIP文明沿着直线状的河发展,当NOIP文明毁灭的时候,这块地表变成了平地。NOIP文明的遗迹可以看作坐标平面的x轴。y轴为高度。也就是说,在坐标平面上,直线y=0为地表,y>0的区域为地面上方,y<0的区域为地面下方。此外,从NOIP文明毁灭的时候算起,a年前(a>=0)的地层在直线y=-a的位置上。

     

    NOIP文明毁灭后,NOIP文明的遗迹上发生了Q次地壳变动。第i次(1<=i<=Q)地壳变动用位置Xi,方向Di和变动量Li表示。Di为1或者2。第i次地壳变动以下文所述方式进行:

    • 地层的移动方式如下:

    ○ Di=1时,沿着经过点(Xi, 0),斜率为1的直线形成断层,在这条直线上方的地层会沿着直线向上移动Li。也就是说,这条直线上方的点(x,y)会移动到(x+Li, y+Li)。

    ○ Di=2时,沿着经过点(Xi, 0),斜率为-1的直线形成断层,在这条直线上方的地层会沿着直线向上移动Li。也就是说,这条直线上方的点(x,y)会移动到(x-Li, y+Li)。

    • 此后,y>0区域的地层会风化作用全部消失。

     

    时光轮转,回到现代。考古学家LC博士开始发掘NOIP文明的遗迹。LC博士希望知道哪个位置的地表的地层是NOIP文明毁灭前多少年前的地层。目前已经知道发生过哪些地壳变动。你的工作是帮助LC博士计算,对于1<=i<=N的各个整数i,点(i-1, 0)和点(i, 0)之间的地表的地层是NOIP文明毁灭前多少年前的地层。

    请写一个程序,给定NOIP文明的遗迹上发生的地壳变动的信息,对于所有整数i(1<=i<=N),输出点(i-1, 0)和点(i, 0)之间的地表底层是NOIP文明毁灭前多少年的地层。

     

    【输入】

    第一行是两个用空格隔开的整数N和Q。N是要计算的地点的个数,Q是地壳变动发生的回数。

    接下来Q行中第i行(1<=i<=Q)包含3个空格隔开的整数Xi, Di, Li。分别表示第i次地壳变动的位置,方向和变动量。

     

    输出】

        输出一共N行。第i行(1<=i<=N)为一个整数,表示点(i-1, 0)和点(i, 0)之间的地表底层是NOIP文明毁灭前多少年的地层。

    【数据范围】

        30%的数据满足:N<=100,Q<=100,|Xi|<=100,Li=1

    70%的数据满足:N<=3 000,Q<=3 000

    100%的数据满足:1 ≦ N ≦ 200 0001 ≦ Q ≦ 200 000 |Xi |≦ 1 000 000 000

    1 ≦ Di ≦ 2 1 ≦ Li ≦ 1 000 000 000 (1 ≦ i ≦ Q)

    题目特别神!!

    真的特别神!!

    30pts可以直接模拟。把地形拆成若干小块。

    发现,风干的话,就把我们地表出保留的信息全部删除了。太浪费。

    由于最后只要知道地表的信息。

    我们时光倒流。

    把上升改为沉降。

    开始是一条长度为N的线段。

    随着沉降会分成若干段。

    每一个点的深度,就是最后地表这个位置的层号。

    这个怎么维护呢?

    四十五度实在不行。

    考虑翻转坐标系。

    其实 就是,原来的(x,y)->(x-y,x+y)=(x',y')

    那么,现在,斜率为一的操作,就是竖劈一刀。

    斜率为-1的操作,就是横劈一刀。

    ①操作1

    新的纵坐标y'>xi的点都会往右走2*L

    横坐标+2*L

    纵坐标不变。

    ②操作2

    新的横坐标x'<=xi的点都会往下走2*L

    纵坐标-2*L

    横坐标不变。

     

    发现,无论怎么操作,由于是一个后缀前缀的移动,每个点的横坐标、纵坐标的相对大小都不变。

    所以像是一个区间!!

    可以用两棵线段树。一棵维护横坐标,一棵维护纵坐标。

    后缀前缀,就要找到第一个>xi的位置,第一个<=xi的位置。

    直接二分即可。

     

    最后,两棵线段树dfs一遍。

    要还原真实的横坐标。y=(y'-x')/2

    然后,y=-y才是真正的层数(越往下号越大)

     

    代码:

     

    #include<bits/stdc++.h>
    #define mid ((l+r)>>1)
    using namespace std;
    typedef long long ll;
    const int N=200000+5;
    const int inf=0x3f3f3f3f;
    int n,m;
    struct node{
        int xi,di,L;
    }q[N];
    struct tree{
        ll mx,ad;
        #define tr t[c]
    }t[3][4*N];
    void pushup(int c,int x){
        tr[x].mx=max(tr[x<<1].mx,tr[x<<1|1].mx);
    }
    ll p[3][N];
    void build(int c,int x,int l,int r){
        if(l==r){
            tr[x].mx=l;
            tr[x].ad=0;
            return;
        }
        build(c,x<<1,l,mid);
        build(c,x<<1|1,mid+1,r);
        pushup(c,x);
    }
    void pushdown(int c,int x){
        if(!tr[x].ad) return;
        tr[x<<1].ad+=tr[x].ad;
        tr[x<<1].mx+=tr[x].ad;
        tr[x<<1|1].ad+=tr[x].ad;
        tr[x<<1|1].mx+=tr[x].ad;
        tr[x].ad=0;
    } 
    int pos;
    void find1(int x,int l,int r,int k){//t[1]
        if(l==r){
            if(t[1][x].mx<=k) pos=max(pos,l);
            return;
        }
        pushdown(1,x);
        if(t[1][x<<1].mx<=k) {
            pos=max(pos,mid);find1(x<<1|1,mid+1,r,k);
        }
        else {
            find1(x<<1,l,mid,k);
        }
    }
    void find2(int x,int l,int r,int k){//t[2]
        if(l==r){
            if(t[2][x].mx>k) pos=min(pos,l);
            return;
        }
        pushdown(2,x);
        if(t[2][x<<1].mx<=k){
            find2(x<<1|1,mid+1,r,k);
        }
        else {
            find2(x<<1,l,mid,k);
        }
    }
    void add(int c,int x,int l,int r,int L,int R,int val){
        if(L<=l&&r<=R){
            tr[x].mx+=val;
            tr[x].ad+=val;
            return;
        }
        pushdown(c,x);
        if(L<=mid) add(c,x<<1,l,mid,L,R,val);
        if(mid<R) add(c,x<<1|1,mid+1,r,L,R,val);
        pushup(c,x);
    }
    void dfs(int c,int x,int l,int r){
        if(l==r){
            p[c][l]=tr[x].mx;
            return;
        }
        pushdown(c,x);
        dfs(c,x<<1,l,mid);
        dfs(c,x<<1|1,mid+1,r);
    }
    int main(){
        scanf("%d%d",&n,&m);
        //int pos,dir,len;
        for(int i=1;i<=m;i++){
            scanf("%d%d%d",&q[i].xi,&q[i].di,&q[i].L);
        }
        build(1,1,1,n);
        build(2,1,1,n);
        pos=0;//warning!! ! ! ! ! ! ! ! 
        for(int i=m;i>=1;i--){
            if(q[i].di==1){
                pos=0;
                find1(1,1,n,q[i].xi);
                if(pos)add(2,1,1,n,1,pos,-2*q[i].L);
            }
            else{
                pos=inf;
                find2(1,1,n,q[i].xi);
                if(pos!=inf)add(1,1,1,n,pos,n,2*q[i].L);
            }
        }
        dfs(1,1,1,n);
        dfs(2,1,1,n);
        for(int i=1;i<=n;i++){
            ll ans=-(p[2][i]-p[1][i])/2;
            printf("%lld
    ",ans);
        }
        return 0;
    }

     

     

    总结:

    这个题真的是神题。

    放进省选组也可以。

    本身题目很抽象,倾斜直线很难处理。所以倾斜坐标系,变成横平竖直的直线。

    本质上是(x,y)->(x-y,x+y)最后还原真实坐标即可。

    这个技巧要注意!!

     

    然后,由于向上升很麻烦,而且会损失很多存储的信息。我们就让它下沉。

    而最后的位置恰好就是层数。

    时光倒流。

     

    线段树还是比较自然的。

     

     

  • 相关阅读:
    dede标签:定义文件夹
    SendCloud邮件中为什么会显示代发
    中国天气网天气预报接口api
    话说TP框架里的Vendor这目录是干什么用的啊?类库扩展thinkphp3.1版本
    微信公众号tp3.2放进Model无效,几种实例化的方法试过,还是提示无法提供服务...
    WCF服务编程读书笔记(5):操作
    spingmvc relevent article
    testng
    sts 配置
    一个spring mvc 的例子,带源码的,
  • 原文地址:https://www.cnblogs.com/Miracevin/p/9726468.html
Copyright © 2011-2022 走看看