zoukankan      html  css  js  c++  java
  • Luogu P5103 「JOI 2016 Final」断层 树状数组or线段树+脑子

    太神仙了这题。。。


    原来的地面上升,可以倒着操作(时光倒流),转化为地面沉降,最后的答案就是每个点的深度。

    下面的1,2操作均定义为向下沉降(与原题意的变换相反);

    首先这个题目只会操作前缀和后缀,并且只会把前缀中的数(纵坐标)变小(2操作),后缀中的数(横坐标)变大(1操作),所以具有单调性,可以进行二分。(括号中含义的解释见下)

    先把整个坐标系旋转$45$度(逆时针为例),操作1即纵坐标$y>=xi$的点都会往右走$2*l$,横坐标$+2*l$,纵坐标不变,由于有单调性,只会操作后缀;操作2即横坐标$x<=xi$的点都会往下走$2*l$,纵坐标$-2*l$,横坐标不变,由于有单调性,只会操作前缀。

    所以二分一下实际坐标就好了。。注意最后计算深度是$(x-y)/2$

    我的这种二分需要维护一个$mx$区间最大值,二分时看一眼左右子树的$mx$,然后决定向哪一棵子树递归。

    #include<cstdio>
    #include<iostream>
    #define ls (tr<<1)
    #define rs (tr<<1|1)
    #define ll long long
    #define R register ll
    const int N=200010,Inf=0x3f3f3f3f;
    using namespace std;
    char B[1<<15],*S=B,*T=B,ch;
    #define getchar() (S==T&&(T=(S=B)+fread(B,1,1<<15,stdin),S==T)?EOF:*S++)
    inline int g() {
        R ret=0,fix=1; register char ch; while(!isdigit(ch=getchar())) fix=ch=='-'?-1:fix;
        do ret=ret*10+(ch^48); while(isdigit(ch=getchar())); return ret*fix;
    }
    int n,m;
    struct node {int x,d,l;}q[N];
    ll MX[2][N<<2],TG[2][N<<2];
    #define mx MX[c]
    #define tg TG[c]
    inline void build(int c,int tr,int l,int r) {
        if(l==r) {mx[tr]=l; return ;} R md=l+r>>1;
        build(c,ls,l,md),build(c,rs,md+1,r); mx[tr]=max(mx[ls],mx[rs]);
    }
    inline void spread(int c,int tr) { if(!tg[tr]) return ;
        tg[ls]+=tg[tr],tg[rs]+=tg[tr],mx[ls]+=tg[tr],mx[rs]+=tg[tr]; tg[tr]=0;
    } ll pos;
    inline void fx(int tr,int l,int r,int k) {
        if(l==r) {if(MX[0][tr]<=k) pos=max(pos,(ll)l); return ;} spread(0,tr); R md=l+r>>1;
        if(MX[0][ls]<=k) pos=max(pos,md),fx(rs,md+1,r,k); else fx(ls,l,md,k);
    }
    inline void fy(int tr,int l,int r,int k) {
        if(l==r) {if(MX[1][tr]>k) pos=min(pos,(ll)l); return ;} spread(1,tr); R md=l+r>>1;
        if(MX[1][ls]<=k) fy(rs,md+1,r,k); else fy(ls,l,md,k);
    }
    inline void add(int c,int tr,int l,int r,int LL,int RR,int d) {
        if(LL<=l&&r<=RR) {mx[tr]+=d,tg[tr]+=d; return ;} spread(c,tr); R md=l+r>>1;
        if(LL<=md) add(c,ls,l,md,LL,RR,d); if(RR>md) add(c,rs,md+1,r,LL,RR,d); mx[tr]=max(mx[ls],mx[rs]);
    } ll p[2][N];
    inline void calc(int c,int tr,int l,int r) {
        if(l==r) {p[c][l]=mx[tr]; return ;} spread(c,tr); 
        R md=l+r>>1; calc(c,ls,l,md),calc(c,rs,md+1,r);
    }
    signed main() { freopen("geologic.in","r",stdin); freopen("geologic.out","w",stdout);
        n=g(),m=g(); for(R i=1;i<=m;++i) q[i].x=g(),q[i].d=g(),q[i].l=g();
        build(0,1,1,n),build(1,1,1,n); for(R i=m;i>=1;--i) { 
            if(q[i].d==1) {
                pos=0; fx(1,1,n,q[i].x);
                if(pos) add(1,1,1,n,1,pos,-2*q[i].l);
            } else {
                pos=Inf; fy(1,1,n,q[i].x);
                if(pos!=Inf) add(0,1,1,n,pos,n,2*q[i].l);
            } //cerr<<pos<<endl;
        } calc(0,1,1,n),calc(1,1,1,n);
        for(R i=1,ans;i<=n;++i) ans=(p[0][i]-p[1][i])/2,printf("%lld
    ",ans);
    }

    这还有一个不旋转坐标的,具体的就是类似直接模拟,但是难度在如何二分位置;

    想一想发现:这不是直线方程么。。。

    所以还是分别维护横纵坐标,但是二分条件改成$y>=x-xi$即$x-y<=xi$或$y>=-x+xi$即$x+y>=xi$;

    #include<cstdio>
    #include<iostream>
    #define ll long long
    #define R register ll
    const int M=262145;
    char B[1<<15],*S=B,*T=B;
    #define getchar() (S==T&&(T=(S=B)+fread(B,1,1<<15,stdin),S==T)?EOF:*S++)
    using namespace std;
    inline int g() {
        R ret=0,fix=1; register char ch; while(!isdigit(ch=getchar())) fix=ch=='-'?-1:fix;
        do ret=ret*10+(ch^48); while(isdigit(ch=getchar())); return ret*fix;
    } ll x[M],y[M],ans[M];
    inline int fx(ll d) { R pos=0,t;
        for(R i=17;~i;--i) if((t=x[pos+(1<<i)]-y[pos+(1<<i)])<=d) pos+=(1<<i),d-=t; return pos;
    } 
    inline int fy(ll d) { R pos=0,t;
        for(R i=17;~i;--i) if((t=x[pos+(1<<i)]+y[pos+(1<<i)])<=d) pos+=(1<<i),d-=t; return pos;
    } int n,m;
    inline void add(int pos,int incx,int incy) {for(;pos<M;pos+=pos&-pos) x[pos]+=incx,y[pos]+=incy;}
    struct node {int x,d,l;} q[M];
    signed main() { freopen("geologic.in","r",stdin); freopen("geologic.out","w",stdout);
        n=g(),m=g(); for(R i=1;i<=n;++i) add(i,1,0);
        for(R i=1;i<=m;++i) q[i].x=g(),q[i].d=g(),q[i].l=g();
        for(R i=m;i;--i) if(q[i].d==1) {
            R pos=fx(q[i].x); if(pos) add(1,-q[i].l,-q[i].l),add(pos+1,q[i].l,q[i].l);
        } else { R pos=fy(q[i].x); if(pos<n) add(pos+1,q[i].l,-q[i].l);} 
        for(R i=1;i<=n;++i) {
            ans[i]=ans[i-(i&-i)]+y[i];
            printf("%lld
    ",-ans[i]);    
        }
    }

    2019.06.01 June

  • 相关阅读:
    实现a标签按钮完全禁用【转】
    input 内容改变的触发事件【转】
    PHP字符串操作(string替换、删除、截取、复制、连接、比较、查找、包含、大小写转换、切割成数组等)【转】
    数列分块入门 4(涉及区间加法,区间求和)
    #6279. 数列分块入门 3(询问区间内小于某个值 xx 的前驱(比其小的最大元素))
    #6278. 数列分块入门 2(询问区间内小于某个值 xx 的元素个数)
    A Simple Problem with Integers POJ
    bzoj3262: 陌上花开(CDQ+树状数组处理三维偏序问题)
    P3806 【模板】点分治1(CDQ分治)
    Toy Train(贪心)
  • 原文地址:https://www.cnblogs.com/Jackpei/p/10959456.html
Copyright © 2011-2022 走看看