洛谷P4501/loj#2529 [ZJOI2018]胖(ST表+二分)

题面

传送门(loj)

传送门(洛谷)

题解

我们对于每一个与宫殿相连的点,分别计算它会作为多少个点的最短路的起点

若该点为\(u\),对于某个点\(p\)来说,如果\(d=|p-u|\),且在\([p-d,p+d]\)中不存在点到\(p\)的距离小于\(u\)\(p\)的距离,那么\(u\)就可以作为\(p\)的最短路的起点

易知可行的\(p\)肯定是连续的一段区间,所以我们可以二分左右端点

\(sum_i\)表示点\(i\)到点\(1\)的距离,我们维护关键点的区间中\((sum_i-l_i)_{\max}\)\((sum_i+l_i)_{\min}\)。对于\(p\)来说,\([p-d,p]\)中可行的关键点肯定是最大的\(sum_i-l_i\),而\([p,p+d]\)中肯定是最小的\(sum_i+l_i\),用\(ST\)表可以做到\(O(1)\)查询。然后把这两个点和\(u\)比较,如果\(u\)比起它们仍然更优,那么\(u\)就可以占领\(p\)

注意判断一下如果两个关键点到\(p\)的距离相等只能算一个,所以特判一下就好了

//minamoto
#include<bits/stdc++.h>
#define R register
#define ll long long
#define fp(i,a,b) for(R int i=a,I=b+1;i<I;++i)
#define fd(i,a,b) for(R int i=a,I=b-1;i>I;--i)
#define go(u) for(int i=head[u],v=e[i].v;i;i=e[i].nx,v=e[i].v)
using namespace std;
char buf[1<<21],*p1=buf,*p2=buf;
inline char getc(){return p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++;}
int read(){
    R int res,f=1;R char ch;
    while((ch=getc())>'9'||ch<'0')(ch=='-')&&(f=-1);
    for(res=ch-'0';(ch=getc())>='0'&&ch<='9';res=res*10+ch-'0');
    return res*f;
}
char sr[1<<21],z[20];int C=-1,Z=0;
inline void Ot(){fwrite(sr,1,C+1,stdout),C=-1;}
void print(R ll x){
    if(C>1<<20)Ot();if(x<0)sr[++C]='-',x=-x;
    while(z[++Z]=x%10+48,x/=10);
    while(sr[++C]=z[Z],--Z);sr[++C]='\n';
}
const int N=2e5+5;
struct node{
    int u,d;
    node(){}
    node(R int x,R int y):u(x),d(y){}
    inline bool operator <(const node &b)const{return u<b.u;}
}e[N];
ll st1[N][21],st2[N][21],l1[N],l2[N],sum[N];
int Log[N],n,m,k;
inline int Max(R int x,R int y){return l1[x]==l1[y]?max(x,y):l1[x]>l1[y]?x:y;}
inline int Min(R int x,R int y){return l2[x]==l2[y]?min(x,y):l2[x]<l2[y]?x:y;}
inline int get1(int l,int r){
    int k=Log[r-l+1];
    return Max(st1[l][k],st1[r-(1<<k)+1][k]);
}
inline int get2(int l,int r){
    int k=Log[r-l+1];
    return Min(st2[l][k],st2[r-(1<<k)+1][k]);
}
void init(){
    sort(e+1,e+1+k);
    fp(i,1,k){
        l1[i]=sum[e[i].u]-e[i].d,l2[i]=sum[e[i].u]+e[i].d;
        st1[i][0]=st2[i][0]=i;
    }
    for(R int j=1;(1<<j)<=k;++j)
        fp(i,1,k-(1<<j)+1){
            st1[i][j]=Max(st1[i][j-1],st1[i+(1<<(j-1))][j-1]);
            st2[i][j]=Min(st2[i][j-1],st2[i+(1<<(j-1))][j-1]);
        }
}
int better(int x,int y,int p){
    ll ans1=e[x].d+abs(sum[e[x].u]-sum[p]);
    ll ans2=e[y].d+abs(sum[e[y].u]-sum[p]);
    if(ans1!=ans2)return ans1<ans2?x:y;
    if(abs(p-e[x].u)!=abs(p-e[y].u))return abs(p-e[x].u)<abs(p-e[y].u)?x:y;
    return min(x,y);
}
bool ck(int p,int g){
    int d=abs(e[g].u-p);
    int l=lower_bound(e+1,e+1+k,node(p-d,0))-e;
    int r=upper_bound(e+1,e+1+k,node(p+d,0))-e-1;
    int mid=upper_bound(e+1,e+1+k,node(p,0))-e-1;
    if(l<=mid&&better(g,get1(l,mid),p)!=g)return false;
    if(r>mid&&better(g,get2(mid+1,r),p)!=g)return false;
    return true;
}
void solve(){
    ll res=0;
    fp(i,1,k){
        int l=1,r=e[i].u,mid,x,ans;
        while(l<=r)ck(mid=(l+r)>>1,i)?(ans=mid,r=mid-1):(l=mid+1);
        x=ans;
        l=e[i].u,r=n;
        while(l<=r)ck(mid=(l+r)>>1,i)?(ans=mid,l=mid+1):(r=mid-1);
        res+=ans-x+1;
    }
    print(res);
}
int main(){
//  freopen("testdata.in","r",stdin);
    n=read(),m=read();
    fp(i,2,n)sum[i]=read()+sum[i-1],Log[i]=Log[i>>1]+1;
    while(m--){
        k=read();
        fp(i,1,k)e[i].u=read(),e[i].d=read();
        init(),solve();
    }
    return Ot(),0;
}

转载于:https://www.cnblogs.com/bztMinamoto/p/10485500.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值