考古研究——四十五度翻转+时光倒流+线段树二分

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\n",ans);
    }
    return 0;
}

 

 

 

总结:

这个题真的是神题。

放进省选组也可以。

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

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

这个技巧要注意!!

 

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

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

时光倒流。

 

线段树还是比较自然的。

 

 

 

转载于:https://www.cnblogs.com/Miracevin/p/9726468.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值