【BZOJ5308】[ZJOI2018]胖(模拟,ST表,二分)

本文详细解析了BZOJ5308题目【ZJOI2018】胖的解题思路,通过使用模拟、ST表和二分查找算法确定每条边能影响的区间,实现对最短路径的有效更新。文章提供了完整的C++代码示例,展示了如何利用ST表进行区间最大最小值查询,以及如何通过二分查找确定边界。

【BZOJ5308】[ZJOI2018]胖(模拟,ST表,二分)

题面

BZOJ
洛谷

题解

首先发现每条\(0\)出发的边都一定会更新到底下的一段区间的点。
考虑存在一条\(0\rightarrow x\)的边,我们来求解其可以影响的区间\([L,R]\),显然\(L\le x\le R\)
两侧分开考虑,以左边举例。
二分一个\(L\)。如果这个\(L\)可行,即不存在一条边\(0\rightarrow y\),满足\(W_{0\rightarrow x}+Dis(L,x)\ge W_{0\rightarrow y}+Dis(L,y)\),且\(abs(y-L)\le abs(x-L)\)
也就是要么\(x\)出发能够更新最短路,要么就是\(x\)\(L\)更近,所以先被\(x\)的路径更新了一次。
那么对于二分出来的\(L\),令\(len=|x-L|\),如果在\([L-len,L+len]\)区间内存在一个点能够更新出更小的距离则当前\(L\)不可行。\(R\)同理。
然后大力\(ST\)表二分一下就好了。
注意一下如果一个点可以同时被多个点更新的时候,一定要确定好一个顺序关系,使得最终计算出来的结果不重不漏。

#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
#define ll long long
#define MAX 200200
inline int read()
{
    int x=0;bool t=false;char ch=getchar();
    while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
    if(ch=='-')t=true,ch=getchar();
    while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
    return t?-x:x;
}
int n,Q,k,lg[MAX];
ll w[MAX],v1[MAX],v2[MAX];
ll mn[20][MAX],mx[20][MAX];
struct Line{int x,w;}a[MAX];
bool operator<(Line a,Line b){return a.x<b.x;}
int Max(int x,int y)
{
    if(v1[x]!=v1[y])return v1[x]>v1[y]?x:y;
    if(a[x].x!=a[y].x)return a[x].x>a[y].x?x:y;
    return x<y?x:y;
}
int Min(int x,int y)
{
    if(v2[x]!=v2[y])return v2[x]<v2[y]?x:y;
    if(a[x].x!=a[y].x)return a[x].x<a[y].x?x:y;
    return x<y?x:y;
}
void pre()
{
    for(int i=1;i<=k;++i)
        v1[i]=w[a[i].x]-a[i].w,v2[i]=w[a[i].x]+a[i].w;
    for(int i=1;i<=k;++i)mx[0][i]=mn[0][i]=i;
    for(int j=1;j<=lg[k];++j)
        for(int i=1;i+(1<<j)-1<=k;++i)
        {
            mx[j][i]=Max(mx[j-1][i],mx[j-1][i+(1<<(j-1))]);
            mn[j][i]=Min(mn[j-1][i],mn[j-1][i+(1<<(j-1))]);
        }
}
ll RMQ_mx(int l,int r)
{
    int k=lg[r-l+1];
    return Max(mx[k][l],mx[k][r-(1<<k)+1]);
}
ll RMQ_mn(int l,int r)
{
    int k=lg[r-l+1];
    return Min(mn[k][l],mn[k][r-(1<<k)+1]);
}
bool check(int x,int l,int y)
{
    int p=lower_bound(&a[1],&a[k+1],(Line){y-l,0})-a;
    int q=upper_bound(&a[1],&a[k+1],(Line){y+l,0})-a-1;
    int r=lower_bound(&a[1],&a[k+1],(Line){y,0})-a;
    if(r<=q)
    {
        int pos=RMQ_mn(r,q);ll V=abs(w[a[x].x]-w[y])+a[x].w;
        if(v2[pos]-w[y]<V||(v2[pos]-w[y]==V&&abs(a[pos].x-y)<abs(a[x].x-y))
           ||(v2[pos]-w[y]==V&&abs(a[pos].x-y)==abs(a[x].x-y)&&pos<x))
            return false;
    }
    if(p<r)
    {
        int pos=RMQ_mx(p,r-1);ll V=abs(w[a[x].x]-w[y])+a[x].w;
        if(w[y]-v1[pos]<V||(w[y]-v1[pos]==V&&abs(a[pos].x-y)<abs(y-a[x].x))
           ||(w[y]-v1[pos]==V&&abs(a[pos].x-y)==abs(a[x].x-y)&&pos<x))
            return false;
    }
    return true;
}
int GetL(int x)
{
    int l=1,r=a[x].x,ret=a[x].x;
    while(l<=r)
    {
        int mid=(l+r)>>1;
        if(check(x,a[x].x-mid,mid))ret=mid,r=mid-1;
        else l=mid+1;
    }
    return ret;
}
int GetR(int x)
{
    int l=a[x].x,r=n,ret=a[x].x;
    while(l<=r)
    {
        int mid=(l+r)>>1;
        if(check(x,mid-a[x].x,mid))ret=mid,l=mid+1;
        else r=mid-1;
    }
    return ret;
}
ll Solve()
{
    k=read();for(int i=1;i<=k;++i)a[i].x=read(),a[i].w=read();
    sort(&a[1],&a[k+1]);
    pre();ll ans=0;
    for(int i=1;i<=k;++i)
    {
        ans+=GetR(i)-GetL(i)+1;
    }
    return ans;
}
int main()
{
    n=read();Q=read();
    for(int i=2;i<=n;++i)lg[i]=lg[i>>1]+1;
    for(int i=2;i<=n;++i)w[i]=read()+w[i-1];
    while(Q--)printf("%lld\n",Solve());
    return 0;
}

转载于:https://www.cnblogs.com/cjyyb/p/10414922.html

乐播投屏是一款简单好用、功能强大的专业投屏软件,支持手机投屏电视、手机投电脑、电脑投电视等多种投屏方式。 多端兼容与跨网投屏:支持手机、平板、电脑等多种设备之间的自由组合投屏,且无需连接 WiFi,通过跨屏技术打破网络限制,扫一扫即可投屏。 广泛的应用支持:支持 10000+APP 投屏,包括综合视频、网盘与浏览器、美韩剧、斗鱼、虎牙等直播平台,还能将央视、湖南卫视等各大卫视的直播内容一键投屏。 高清流畅投屏体验:腾讯独家智能音画调校技术,支持 4K 高清画质、240Hz 超高帧率,低延迟不卡顿,能为用户提供更高清、流畅的视觉享受。 会议办公功能强大:拥有全球唯一的 “超级投屏空间”,扫码即投,无需安装。支持多人共享投屏、远程协作批注,PPT、Excel、视频等文件都能流畅展示,还具备企业级安全加密,保障会议资料不泄露。 多人互动功能:支持多人投屏,邀请好友加入投屏互动,远程也可加入。同时具备一屏多显、语音互动功能,支持多人连麦,实时语音交流。 文件支持全面:支持 PPT、PDF、Word、Excel 等办公文件,以及视频、图片等多种类型文件的投屏,还支持网盘直投,无需下载和转格式。 特色功能丰富:投屏时可同步录制投屏画面,部分版本还支持通过触控屏或电视端外接鼠标反控电脑,以及在投屏过程中用画笔实时标注等功能。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值