dtoj#3699. 胖(joke)
题目描述:
Cedyks 是九条可怜的好朋友(可能这场比赛公开以后就不是了),也是这题的主人公。
Cedyks 是一个富有的男孩子。他住在著名的The Place(宫殿)中。
Cedyks 是一个努力的男孩子。他每天都做着不一样的题来锻炼他的The Salt(灵魂)。
这天,他打算在他的宫殿外围修筑一道城墙,城墙上有 $ n $ 座瞭望塔。你可以把城墙看做一条线段,瞭望塔是线段上的 $ n $ 个点,其中 $ 1 $ 和 $ n $ 分别为城墙的两个端点。其中第 $ i $ 座瞭望塔和第 $ i+1 $ 座瞭望塔的距离为 $ w_i $ ,他们之间的道路是双向的。
城墙很快就修建好了,现在Cedyks 开始计划修筑他的宫殿到城墙的道路。因为这题的题目名称,Cedyks 打算用他的宫殿到每一个瞭望塔的最短道路之和来衡量一个修建计划。
现在Cedyks 手上有 $ m $ 个设计方案,第 $ k $ 个设计方案会在宫殿和瞭望塔之间修建 $ T_k $ 条双向道路,第 $ i $ 条道路连接着瞭望塔 $ a_i $ ,长度为 $ l_i $ 。
计算到每一个瞭望塔的最短路之和是一个繁重的工程,本来Cedyks 想用广为流传的SPFA算法来求解,但是因为他的butter(缓冲区)实在是太小了,他只能转而用原始的贝尔福特曼算法来计算,算法的流程大概如下:
$ 1. $ 定义宫殿是 $ 0 $ 号点,第 $ i $ 个瞭望塔是 $ i $ 号点,双向边 $ u_i,v_i,l_i $ 为一条连接 $ u_i $ 和 $ v_i $ 的双向道路。令 $ d $ 为距离数组,最开始 $ d_0=0,d_i=10^{18}(i \in [1,n]) $ 。
$ 2. $ 令辅助数组 $ c=d $ 。依次对于每一条边 $ u_i,v_i,w_i $ 进行增广, $ c_{u_i}=min(c\_{u_i},d_{v_i}+w_i),c_{v_i}=min(c_{v_i},d_{u_i}+w_i) $ 。
$ 3. $ 令 $ t $ 为 $ c $ 和 $ d $ 中不一样位置个数,即令 $ S=\{i|c_i \neq d_i\} $ ,则 $ t=|S| $ 。若 $ t=0 $ ,说明 $ d $ 就是最终的最短路,算法结束。否则令 $ d=c $ ,回到第二步。
因为需要计算的设计方案实在是太多了,所以Cedyks 雇佣了一些人来帮他进行计算。为了避免这些人用捏造出来的数据偷懒,他定义一个设计方案的校验值为在这个方案上运行贝尔福特曼算法每一次进入第三步 $ t $ 的和。他会让好几个雇佣来的人计算同样的设计方案,并比对每一个人给出的校验值。
你是Cedyks 雇佣来的苦力之一,聪明的你发现在这个情形下计算最短路的长度的和是一件非常简单的事情。但是寄人篱下不得不低头,你不得不再计算出每一个方案的校验值来交差。
思路:
对于一个瞭望塔,他所能覆盖的一定是一段连续的区间,所以我们可以通过二分找到它的左右端点。
对于一个端点 $x$ 它能被瞭望塔 $p$ 覆盖的条件是:
令 $p,x$ 的距离为 $d$
在 $[x-d,x+d]$ 的范围内没有其他瞭望塔到 $x$ 的距离小于 $p$ 到 $x$ 的距离。(等于的情况有特别处理,后文说明)。
那么意味着我们要求一段连续区间内的瞭望塔到某个单点的距离的最小值,如果我们令 $dis[x]$ 表示端点 $x$ 到 $1$ 的距离,那么瞭望塔到单点的距离等于 $|dis[p]-dis[x]|+l$ 。为了去掉绝对值,我们对于瞭望塔在右的情况记录 $dis[p]+l$ ,并用 $st$ 表维护区间最小值。对于瞭望塔在左的情况记录 $dis[p]-l$ 维护区间最大值。
接下来对于距离相等的情况,首先如果是瞭望塔右侧的部分我们可以优先选择距离最贴近瞭望塔的,也就是坐标小的,在左侧的类比。
如果对于在瞭望塔两边且到瞭望塔的坐标相等的,我们可以规定选择小的那一边,这样就能保证只被选择一次。
以下代码:

#include<bits/stdc++.h> #define il inline #define LL long long #define _(d) while(d(isdigit(ch=getchar()))) using namespace std; const int N=2e5+5; LL dis[N],v1[N],v2[N]; int n,m,k,Lg[N],st1[N][21],st2[N][21],p[N]; struct node{ int x,l; }t[N]; il int read(){ int x,f=1;char ch; _(!)ch=='-'?f=-1:f;x=ch^48; _()x=(x<<1)+(x<<3)+(ch^48); return f*x; } bool cmp(node t1,node t2){ return t1.x<t2.x; } il int Min(int t1,int t2){ if(v1[t1]==v1[t2])return t1<t2?t1:t2; return (v1[t1]<v1[t2])?t1:t2; } il int Max(int t1,int t2){ if(v2[t1]==v2[t2])return t1<t2?t2:t1; return (v2[t1]>v2[t2])?t1:t2; } il int getmn(int l,int r){ int k=Lg[r-l+1]; return Min(st1[l][k],st1[r-(1<<k)+1][k]); } il int getmx(int l,int r){ int k=Lg[r-l+1]; return Max(st2[l][k],st2[r-(1<<k)+1][k]); } il bool pd1(int x,int y){ int l=(x<<1)-p[y],r=y,mid; l=lower_bound(p+1,p+1+k,l)-p; mid=upper_bound(p+1,p+1+k,x)-p-1; LL len=abs(dis[p[y]]-dis[x])+t[y].l; if(l<=mid){ int t1=getmx(l,mid); if(dis[x]-v2[t1]<len)return 0; if(dis[x]-v2[t1]==len&&y!=t1){ if(abs(p[t1]-x)<abs(p[y]-x))return 0; if(abs(p[t1]-x)==abs(p[y]-x)&&t1<y)return 0; } } if(mid<r){ int t2=getmn(mid+1,r); if(v1[t2]-dis[x]<len)return 0; if(v1[t2]-dis[x]==len&&y!=t2){ if(abs(p[t2]-x)<abs(p[y]-x))return 0; if(abs(p[t2]-x)==abs(p[y]-x)&&t2<y)return 0; } } return 1; } il bool pd2(int x,int y){ int l=y,r=(x<<1)-p[y],mid; r=upper_bound(p+1,p+1+k,r)-p-1; mid=upper_bound(p+1,p+1+k,x)-p-1; LL len=abs(dis[p[y]]-dis[x])+t[y].l; if(l<=mid){ int t1=getmx(l,mid); if(dis[x]-v2[t1]<len)return 0; if(dis[x]-v2[t1]==len&&y!=t1){ if(abs(p[t1]-x)<abs(p[y]-x))return 0; if(abs(p[t1]-x)==abs(p[y]-x)&&t1<y)return 0; } } if(mid<r){ int t2=getmn(mid+1,r); if(v1[t2]-dis[x]<len)return 0; if(v1[t2]-dis[x]==len&&y!=t2){ if(abs(p[t2]-x)<abs(p[y]-x))return 0; if(abs(p[t2]-x)==abs(p[y]-x)&&t2<y)return 0; } } return 1; } int main() { n=read();m=read(); for(int i=2;i<=n;i++)dis[i]=dis[i-1]+read(),Lg[i]=Lg[i>>1]+1; for(int i=1;i<=m;i++){ k=read(); for(int i=1;i<=k;i++)t[i].x=read(),t[i].l=read(); sort(t+1,t+1+k,cmp); for(int i=1;i<=k;i++)p[i]=t[i].x; for(int i=1;i<=k;i++) v1[i]=t[i].l+dis[t[i].x],v2[i]=dis[t[i].x]-t[i].l; for(int i=1;i<=k;i++)st1[i][0]=i,st2[i][0]=i; for(int j=1;j<=Lg[k];j++)for(int i=1;i+(1<<j)-1<=k;i++) st1[i][j]=Min(st1[i][j-1],st1[i+(1<<(j-1))][j-1]), st2[i][j]=Max(st2[i][j-1],st2[i+(1<<(j-1))][j-1]); LL ans=0; for(int i=1;i<=k;i++){ int l=1,r=t[i].x,res=r; while(l<=r){ int mid=(l+r)>>1; if(pd1(mid,i))res=mid,r=mid-1; else l=mid+1; } int kk=res; l=t[i].x,r=n,res=l; while(l<=r){ int mid=(l+r)>>1; if(pd2(mid,i))res=mid,l=mid+1; else r=mid-1; } ans+=res-kk+1; } printf("%lld\n",ans); } return 0; }