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

原文地址:https://www.cnblogs.com/Jackpei/p/10959456.html