洛谷P4501 [ZJOI2018] 胖

求出每个新修道路到达的瞭望塔 \(x\) 能更新到的区间 \([l_i,r_i]\),答案即为 \(\sum r_i-l_i+1\)

发现区间的范围是可以二分出来的,考虑 \(x\) 是否能更新 \(p\),设 \(d=|x-p|\),若在区间 \([p-d,p+d]\) 中没有比 \(x\) 更优的瞭望塔,则 \(p\) 能被 \(x\) 更新。判定是否更优可以用 \(ST\) 表查询最值。

当两个点同时可以更新一个点时会算重,设下标小的点更优即可。

#include<bits/stdc++.h>
#define maxn 200010
#define inf 1000000000000000
using namespace std;
typedef long long ll;
template<typename T> inline void read(T &x)
{
    x=0;char c=getchar();bool flag=false;
    while(!isdigit(c)){if(c=='-')flag=true;c=getchar();}
    while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
    if(flag)x=-x;
}
int n,m,k;
int lg[maxn];
ll w[maxn],st[2][maxn][20];
struct node
{
    int pos;
    ll v;
}e[maxn];
bool operator < (const node &a,const node &b)
{
    return a.pos<b.pos;
}
void ST()
{
    sort(e+1,e+k+1);
    for(int i=1;i<=k;++i)
        st[0][i][0]=e[i].v-w[e[i].pos],st[1][i][0]=e[i].v+w[e[i].pos];
    for(int t=0;t<=1;++t)
        for(int j=1;j<=17;++j)
            for(int i=1;i+(1<<j)-1<=k;++i)
                st[t][i][j]=min(st[t][i][j-1],st[t][i+(1<<(j-1))][j-1]);
}
ll query(int l,int r,int t)
{
    if(l>r) swap(l,r);
    l=lower_bound(e+1,e+k+1,(node){max(l,1),0})-e;
    r=upper_bound(e+1,e+k+1,(node){min(r,n),0})-e-1;
    if(l>r) return inf;
    int len=lg[r-l+1];
    return min(st[t][l][len],st[t][r-(1<<len)+1][len]);
}
bool check(int x,int p,int t)
{
    if(x==p) return true;
    ll v=query(x,x,t^1)+w[p]*(t?1:-1);
    if(!t&&(v>=query(2*p-x+1,p,0)+w[p]||v>=query(p,x-1,1)-w[p])) return false;
    if(t&&(v>=query(x+1,p,0)+w[p]||v>=query(p,2*p-x-1,1)-w[p])) return false;
    return v-t<query(2*p-x,2*p-x,t)+w[p]*(t?-1:1);
}
int solve(int x,int t)
{
    int l=t?x:1,r=t?n:x,pos=x;
    while(l<=r)
    {
        int mid=(l+r)>>1;
        if(check(x,mid,t)) pos=mid,t?l=mid+1:r=mid-1;
        else t?r=mid-1:l=mid+1;
    }
    return pos;
}
int main()
{
    read(n),read(m),lg[0]=-1;
    for(int i=1;i<=n;++i) lg[i]=lg[i>>1]+1;
    for(int i=2;i<=n;++i) read(w[i]),w[i]+=w[i-1];
    while(m--)
    {
        ll v=0;
        read(k);
        for(int i=1;i<=k;++i) read(e[i].pos),read(e[i].v);
        ST();
        for(int i=1;i<=k;++i) v+=solve(e[i].pos,1)-solve(e[i].pos,0)+1;
        printf("%lld\n",v);
    }
    return 0;
}
posted @ 2020-11-12 22:25  lhm_liu  阅读(132)  评论(0编辑  收藏  举报