码迷,mamicode.com
首页 > 其他好文 > 详细

bzoj 5308: [Zjoi2018]胖

时间:2018-05-22 18:21:55      阅读:226      评论:0      收藏:0      [点我收藏+]

标签:\n   第三关   说明   getchar   script   oid   printf   辅助   uil   

Description

Cedyks是九条可怜的好朋友(可能这场比赛公开以后就不是了),也是这题的主人公。
Cedyks是一个富有的男孩子。他住在著名的ThePLace(宫殿)中。
Cedyks是一个努力的男孩子。他每天都做着不一样的题来锻炼他的The SaLt(灵魂)。
这天,他打算在他的宫殿外围修筑一道城墙,城墙上有n座瞭望塔。
你可以把城墙看做一条线段,瞭望塔是线段上的n个点,其中1和n分别为城墙的两个端点。
其中第i座瞭望塔和第i+1座瞭望塔的距离为wi,他们之间的道路是双向的。
城墙很快就修建好了,现在Cedyks开始计划修筑他的宫殿到城墙的道路。
因为这题的题目名称,
Cedyks打算用他的宫殿到每一个瞭望塔的最短道路之和来衡量一个修建计划。
现在Cedyks手上有m个设计方案,第k个设计方案会在宫殿和瞭望塔之间修建Tk条双向道路,
第i条道路连接着瞭望塔ai,长度为Li。
计算到每一个瞭望塔的最短路之和是一个繁重的工程,本来Cedyks想用广为流传的SPFA算法
来求解,但是因为他的butter(缓冲区)实在是太小了,他只能转而用原始的贝尔福特曼算法
来计算,算法的流程大概如下:
1:定义宫殿是0号点,第i个瞭望塔是i号点,双向边(ui,vi,Li)为一条连接ui和vi的双向道路。
令d为距离数组,最开始d0=0,di=10^18(i∈[1,n])。
2:令辅助数组c=d。依次对于每一条边(ui,vi,wi)进行增广,
cui=min(cui,dvi+wi),
cvi=min(cvi,dui+wi)。
3:令t为c和d中不一样的位置个数,即令S={i|ci!=di},则t=S。若t=0,说明d
就是最终的最短路,算法结束。否则令d=c,回到第二步。
因为需要计算的设计方案实在是太多了,所以Cedyks雇佣了一些人来帮他进行计算。
为了避免这些人用捏造出来的数据偷懒,他定义一个设计方案的校验值为在这个方案
上运行贝尔福特曼算法每一次进入第三步t的和。他会让好几个雇佣来的人计算同样
的设计方案,并比对每一个人给出的校验值。
你是Cedyks雇佣来的苦力之一,聪明的你发现在这个情形下计算最短路的长度的和
是一件非常简单的事情。但是寄人篱下不得不低头,你不得不再计算出每一个方案
的校验值来交差。

Solution

考场上不会判一个点相同时间被相同距离更新多次的情况...
只需要考虑关键点能够更新到的点就可以了
显然能更新到的点是一个区间
二分这个左右端点
如何判断一个点 \(y\) ,能否被关键点 \(x\) 更新?
\(d=|x-y|\)
判断 \([y-d,y+d]\) 之间是否存在其他的关键点到 \(y\) 的距离更小
分别考虑 \(y\) 左右两边的情况:
以左边为例,设可以更新 \(y\) 的点为 \(p\),\(w\) 为到 \(1\) 的距离:
要查找 \(w[x]-w[p]+l\) 的最小值,维护一个 \(max(w[p]-l)\) 即可

注意当两个关键点同时可以更新一个点的时候,要以更新的时间为第二关键字,编号为第三关键字

#include<bits/stdc++.h>
using namespace std;
template<class T>void gi(T &x){
    int f;char c;
    for(f=1,c=getchar();c<'0'||c>'9';c=getchar())if(c=='-')f=-1;
    for(x=0;c<='9'&&c>='0';c=getchar())x=x*10+(c&15);x*=f;
}
typedef long long ll;
const int N=2e5+10;
int n,Q,m,L[N],R[N],mx,Log[N],sx[N][20],sd[N][20];ll w[N],v1[N],v2[N];
struct data{int x,l;}a[N];
inline bool operator <(data i,data j){return i.x<j.x;}
inline int Max(int p,int q){
    if(v1[p]!=v1[q])return v1[p]>v1[q]?p:q;
    if(a[p].x!=a[q].x)return a[p].x>a[q].x?p:q;
    return p<q?p:q;
}
inline int Min(int p,int q){
    if(v2[p]!=v2[q])return v2[p]<v2[q]?p:q;
    if(a[p].x!=a[q].x)return a[p].x<a[q].x?p:q;
    return p<q?p:q;
}
inline void build(){
    mx=Log[m]+1;
    for(int i=1;i<=m;i++){
        sd[i][0]=i,sx[i][0]=i;
        v1[i]=w[a[i].x]-a[i].l,v2[i]=w[a[i].x]+a[i].l;
    }
    for(int j=1;j<=mx;j++)
        for(int i=1;i+(1<<j)-1<=m;i++){
            int k=i+(1<<(j-1));
            sd[i][j]=Max(sd[i][j-1],sd[k][j-1]);
            sx[i][j]=Min(sx[i][j-1],sx[k][j-1]);
        }
}
inline ll qx(int l,int r){
    int k=Log[r-l+1];
    return Min(sx[l][k],sx[r-(1<<k)+1][k]);
}
inline ll qd(int l,int r){
    int k=Log[r-l+1];
    return Max(sd[l][k],sd[r-(1<<k)+1][k]);
}
inline bool check(int x,int d,int y){
    int p=lower_bound(a+1,a+m+1,(data){y-d,0})-a;
    int q=upper_bound(a+1,a+m+1,(data){y+d,0})-a-1;
    int z=lower_bound(a+1,a+m+1,(data){y,0})-a;
    if(q>=z){
        int t1=qx(z,q);ll vr=abs(w[y]-w[a[x].x])+a[x].l;
        if(v2[t1]-w[y]<vr || (v2[t1]-w[y]==vr && abs(a[t1].x-y)<abs(y-a[x].x)))return false;
        if(v2[t1]-w[y]==vr && abs(a[t1].x-y)==abs(y-a[x].x) && t1<x)return false;
    }
    if(p<z){
        int t1=qd(p,z-1);ll vr=abs(w[y]-w[a[x].x])+a[x].l;
        if(w[y]-v1[t1]<vr || (w[y]-v1[t1]==vr && abs(a[t1].x-y)<abs(y-a[x].x)))return false;
        if(w[y]-v1[t1]==vr && abs(a[t1].x-y)==abs(y-a[x].x) && t1<x)return false;
    }
    return true;
}
inline int getL(int x){
    int l=1,r=a[x].x,mid,ret=r;
    while(l<=r){
        mid=(l+r)>>1;
        if(check(x,a[x].x-mid,mid))ret=mid,r=mid-1;
        else l=mid+1;
    }
    return ret;
}
inline int getR(int x){
    int l=a[x].x,r=n,mid,ret=l;
    while(l<=r){
        mid=(l+r)>>1;
        if(check(x,mid-a[x].x,mid))ret=mid,l=mid+1;
        else r=mid-1;
    }
    return ret;
}
inline void solve(){
    gi(m);
    for(int i=1;i<=m;i++)gi(a[i].x),gi(a[i].l);
    sort(a+1,a+m+1);
    build();
    ll ans=0;
    for(int i=1;i<=m;i++)L[i]=getL(i),R[i]=getR(i),ans+=R[i]-L[i]+1;
    printf("%lld\n",ans);
}
int main(){
  freopen("pp.in","r",stdin);
  freopen("pp.out","w",stdout);
  cin>>n>>Q;
  for(int i=2;i<=n;i++)Log[i]=Log[i>>1]+1;
  for(int i=2;i<=n;i++)gi(w[i]),w[i]+=w[i-1];
  while(Q--)solve();
  return 0;
}

bzoj 5308: [Zjoi2018]胖

标签:\n   第三关   说明   getchar   script   oid   printf   辅助   uil   

原文地址:https://www.cnblogs.com/Yuzao/p/9073124.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!