「ZJOI2018」胖(ST表+二分)

「ZJOI2018」胖(ST表+二分)

不開 \(O_2\) 又沒卡過去是種怎麼體驗。。。

這可能是 \(ZJOI2018\) 最簡單的一題了。。。我都能 \(A\)。。。

首先我們發現這個奇怪的圖每個點擴展的是一個區間 \([L,R]\),然後我們就可以二分端點了。

一個點 \(x\) 擴展到點 \(y\) 至少要 \(|x-y|\) 的時間,所以我們把 \(a_i\) 排個序,在上面二分一個合法的區間使得 \(|x-a_l|\leq t\)\(|x-a_r|\leq t\)

然後若能擴展到 \(y\),那麼 \(0\) 號點到 \(y\) 號點的距離爲 \(|dis_y-dis_x|+l\)。我們用兩個 \(ST\) 表把絕對值拆掉,分別維護最小值即可。

時間複雜度 \(O(n\log^2 n)\)

爲什麼 \(dl\) 出題人會出到 \(2\times 10^5\)。。。兩個 \(\log\) 一般只出到 \(10^5\) 的啊。。。

\(Code\ Below:\)

// luogu-judger-enable-o2
#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn=200000+10;
int n,m,k,w[maxn],lg[maxn];ll dis[maxn];

struct node{
    int p;ll l;
    node(int p=0,ll l=0):p(p),l(l){}
}a[maxn];
inline bool operator < (const node &a,const node &b){
    return a.p<b.p;
}

struct Sparse_Table{
    ll st[maxn][18];
    inline void init(){
        for(int i=1;i<=k;i++) st[i][0]=a[i].l;
        for(int j=1;j<=lg[k];j++)
            for(int i=1;i+(1<<j)-1<=k;i++) st[i][j]=min(st[i][j-1],st[i+(1<<(j-1))][j-1]);
    }
    inline ll query(int l,int r){
        l=max(l,1);r=min(r,n);
        l=lower_bound(a+1,a+k+1,node(l))-a;
        r=upper_bound(a+1,a+k+1,node(r))-a-1;
        if(l>r) return 1e18;
        int k=lg[r-l+1];
        return min(st[l][k],st[r-(1<<k)+1][k]);
    }
}L,R;

inline int read(){
    register int x=0,f=1;char ch=getchar();
    while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
    while(isdigit(ch)){x=(x<<3)+(x<<1)+ch-'0';ch=getchar();}
    return (f==1)?x:-x;
}


inline bool check1(int x,int y){
//  (2*y-x,y,x]
    if(x==y) return 1;
    ll a=L.query(2*y-x+1,y)+dis[y];
    ll b=R.query(y,x-1)-dis[y];
    ll c=R.query(x,x)-dis[y];
    if(a<=c||b<=c) return 0;
    if(2*y-x>=1) return L.query(2*y-x,2*y-x)+dis[y]>=c;
    return 1;
}

inline bool check2(int x,int y){
//  [x,y,2*y-x)
    if(x==y) return 1;
    ll a=L.query(x+1,y)+dis[y];
    ll b=R.query(y,2*y-x-1)-dis[y];
    ll c=L.query(x,x)+dis[y];
    if(a<=c||b<=c) return 0;
    if(2*y-x<=n) return R.query(2*y-x,2*y-x)-dis[y]>c;
    return 1;
}

inline int solve1(int x){
    int l=1,r=x,mid,ans=0;
    while(l<=r){
        mid=(l+r)>>1;
        if(check1(x,mid)) r=mid-1,ans=mid;
        else l=mid+1;
    }
    return ans;
}

inline int solve2(int x){
    int l=x,r=n,mid,ans=0;
    while(l<=r){
        mid=(l+r)>>1;
        if(check2(x,mid)) l=mid+1,ans=mid;
        else r=mid-1;
    }
    return ans;
}

int main()
{
    n=read(),m=read();
    for(int i=2;i<=n;i++) lg[i]=lg[i>>1]+1;
    for(int i=2;i<=n;i++){
        w[i]=read();
        dis[i]=dis[i-1]+w[i];
    }
    ll ans;
    while(m--){
        k=read();
        for(int i=1;i<=k;i++) a[i].p=read(),a[i].l=read();
        sort(a+1,a+k+1);ans=0;
        for(int i=1;i<=k;i++) a[i].l-=dis[a[i].p];L.init();
        for(int i=1;i<=k;i++) a[i].l+=2*dis[a[i].p];R.init();
        for(int i=1;i<=k;i++) ans+=solve2(a[i].p)-solve1(a[i].p)+1;
        printf("%lld\n",ans);
    }
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章