JOISC2019 時をかけるビ太郎 / Bitaro, who Leaps through Time / 穿越时空 Bitaro

Educational。

首先全部变换成 \([l_i-i-1,r_i-i-1]\)。然后相当于就是对于一个初始的 $a $,然后我们不断做 \(a=\max (a,l)\)\(a=\min(a,r)\),其中后者对答案会产生贡献。

考虑做一个信息的合并。对于两个相交的区间,我们发现它和这两个区间的交是等价的。而对于两个不交的区间 \(P_1,P_2\),我们依次走过 \(P_1,P_2\),我们发现无论我们从哪里开始走,经过这两个区间后得到的 \(x'\) 都和初始的 \(x\) 无关。而我们从初始往右走,在走到第一次区间无交集前,\(a\) 的贡献一定可以用一个 \(\max(0,a-x)\) 表示;走到第一次区间无交集之后,贡献就固定下来了。所以我们考虑记录在第一次遇到无交集前的 \(x\) 以及最后会从哪里出去 \(y\)

我们考虑如何去描述这样的信息。我们可以敏锐地观察到,这样的信息是满足结合率的!所以对于链上的一个区间 \([x,y]\),区间 \([l_x,r_x],[l_{x+1},r_{x+1}],\dots,[l_{y,r_y}]\) 合并出来的结果只有两种:

  • 这些区间存在一个交集。于是我们记录这些区间的交集,记其为 \((A,x,y)\)
  • 这些区间不存在交集。于是我们记录第一次无交集前的 \(x=\min(r)\),以及最后会从哪来出去 \(y\),以及第一次无交集之后的贡献 \(z\),记其为 \((B,x,y,z)\)

我们定义一个函数 \(f(a,S)\),其中 \(S\)\(A\)\(B\) 类型,返回对于初始的 \(a\),经过 \(S\) 之后的位置。同理 \(g(a,S)\) 表示贡献。

现在考虑这些信息的合并:

  • \(A_l+A_r\)
    1. \(x_r\le y_l\) 时,结果为 \((A,x_r,y_l)\)
    2. \(y_l\le x_r\) 时,结果为 \((B,y_l,x_r,0)\)
    3. \(y_r\le x_l\) 时,结果为 \((B,x_l,y_r,x_l-y_r)\)
  • \(B_l+B_r=(B,x_l,f(y_l,B_r),g(y_l,B_r)+z_l+z_r)\)
  • \(B_l+A_r=(B,x_l,f(y_l,A_r),g(y_l,A_r)+z_l)\)
  • \(A_l+B_r\)
    1. \(y _l\le x_r\) 时,结果为 \((B,y_l,y_r,z_r)\)
    2. \(x_r\le x_l\) 时,结果为 \((B,x_l,y_r,x_l-x_r+z_r)\)
    3. 否则,结果为 \(B_r\)
#include<bits/stdc++.h>
#define int long long
#define rep(i,a,b) for(int i=(a);i<=(b);i++)
#define per(i,a,b) for(int i=(a);i>=(b);i--)
#define fi first
#define se second
#define eb emplace_back
#define popc __builtin_popcount
#define sgn(x) ((x)&1?-1:1)
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
typedef vector<int> vi;
typedef vector<pii> vp;
typedef unsigned long long ull;
typedef long double ld;

int read() {
    int x=0,w=1; char c=getchar(); 
    while(!isdigit(c)) {if(c=='-') w=-1; c=getchar();}
    while(isdigit(c)) {x=x*10+c-'0'; c=getchar();}
    return x*w;
}

const int N=3e5+9,inf=0x3f3f3f3f;

int n,m,al[N],ar[N],yl[N],yr[N],a[N],b[N],c[N],d[N],t[N],ans[N];

struct Nod {
    int x,y,z; //!~z => A, ~z => B
    Nod(int _x=0,int _y=inf,int _z=-1) {x=_x,y=_y,z=_z;}
    int f(int a) {return ~z?y:(a<=x?x:(a>=y?y:a));}
    int g(int a) {return ~z?(a<=x?z:z+a-x):(a<=y?0:a-y);}
};
Nod operator + (Nod l,Nod r) {
    if((~l.z)&&(~r.z)) return Nod(l.x,r.y,l.z+r.g(l.y));
    else if(~l.z) return Nod(l.x,r.f(l.y),l.z+r.g(l.y));
    else if(~r.z) return l.y<=r.x?Nod(l.y,r.y,r.z):(r.x<=l.x?Nod(l.x,r.y,l.x-r.x+r.z):r);
    else return l.y<=r.x?Nod(l.y,r.x,0):(r.y<=l.x?Nod(l.x,r.y,l.x-r.y):Nod(max(l.x,r.x),min(l.y,r.y),-1));
}
namespace SegT {
    int ls[N<<1],rs[N<<1],tot; Nod s[N<<1];
    void build(int p,int l,int r) {
        if(l==r) {s[p]=Nod(al[l],ar[l],-1); return;} int mid=l+r>>1;
        build(ls[p]=++tot,l,mid), build(rs[p]=++tot,mid+1,r); s[p]=s[ls[p]]+s[rs[p]];
    }
    void mdf(int p,int l,int r,int x) {
        if(l==r) {s[p]=Nod(al[l],ar[l],-1); return;} int mid=l+r>>1;
        if(x<=mid) mdf(ls[p],l,mid,x); else mdf(rs[p],mid+1,r,x); s[p]=s[ls[p]]+s[rs[p]];
    }
    Nod qry(int p,int l,int r,int x,int y) {
        if(x>y) return Nod();
        if(l==x&&r==y) return s[p]; int mid=l+r>>1;
        if(y<=mid) return qry(ls[p],l,mid,x,y);
        else if(x>mid) return qry(rs[p],mid+1,r,x,y);
        else return qry(ls[p],l,mid,x,mid)+qry(rs[p],mid+1,r,mid+1,y);
    }

    void init() {tot=1; build(1,1,n-1);}
    void update(int x) {mdf(1,1,n-1,x);}
    int query(int u,int tu,int v,int tv) {
        tu-=u, tv-=v;
        Nod s=qry(1,1,n-1,u,v-1);
        int p=s.f(tu), w=s.g(tu);
        return max(0ll,p-tv)+w;
    }
}

void work() {
    rep(i,1,n-1) al[i]=yl[i]-i, ar[i]=yr[i]-i-1;
    SegT::init();
    rep(i,1,m) {
        if(t[i]==1) al[a[i]]=b[i]-a[i], ar[a[i]]=c[i]-a[i]-1, SegT::update(a[i]);
        else if(a[i]<=c[i]) ans[i]=SegT::query(a[i],b[i],c[i],d[i]);
    }
}

signed main() {
    n=read(), m=read();
    rep(i,1,n-1) yl[i]=read(), yr[i]=read();
    rep(i,1,m) {
        t[i]=read();
        if(t[i]==1) a[i]=read(), b[i]=read(), c[i]=read();
        else a[i]=read(), b[i]=read(), c[i]=read(), d[i]=read();
    }
    if(n==1) {
        rep(i,1,m) printf("%lld\n",max(0ll,b[i]-d[i]));
        return 0;
    }
    work();
    reverse(yl+1,yl+n), reverse(yr+1,yr+n);
    rep(i,1,m) if(t[i]==1) a[i]=n-a[i];
    rep(i,1,m) if(t[i]==2) a[i]=n-a[i]+1, c[i]=n-c[i]+1;
    work();
    rep(i,1,m) if(t[i]==2) printf("%lld\n",ans[i]);
    return 0;
}
posted @ 2023-03-01 11:16  LarsWerner  阅读(34)  评论(0编辑  收藏  举报