Luogu4501 [ZJOI2018]胖

Luogu4501 [ZJOI2018]胖

膜拜\(\operatorname{zhouchenyuan}\)秒杀这道题。

线段树

我们可以观察一下Bellman-Ford算法在这张图上是如何进行松弛的,对于一个拥有从\(0\)号点来的边的瞭望塔\(a_i\)来说,它在第一步一定会被边\(0-u\)松弛,随后向两边扩张,直到遇到阻碍后停止,形成一段\(a_i\)松弛过的区间,这个区间的边界之后可能被两边的瞭望塔重新松弛。

我们发现,如果我们能够对于每一个\(a_i\),找到对应的一段区间,那么我们就可以把每个瞭望塔的贡献全部计算出来,从而得到答案。

我们可以二分答案,然后判断是否合法,对于一个\(a_i\),我们通过二分求出其左右边界,先看如何处理左边界:

假设我们二分到的区间为\([g,a_i]\),我们需要保证两个条件:

\(1.\)\(a_i\)不能被区间\([g,a_i)\)中的瞭望塔拦截。

\(2.\)\(a_i\)到达\(g\)点的路程小于在它之前\(g\)左侧瞭望塔到达\(g\)点的路程。

直接处理第一个条件并不容易,但是我们可以利用第一个条件计算出二分边界,就可以丢掉这个限制。

\(s_k=\sum_{i=1}^{k-1} w_i\),我们可以利用\(s_i-s_j\)表示\(i,j\)之间的距离。

对于一个瞭望塔\(a_j\),如果\(j\)不会拦截\(i\),那么就满足:

\[l_i+s_i-s_j<l_j\\ l_i+s_i<l_j+s_j \]

我们用一棵线段树维护区间\(l_i+s_i\)的最小值(无瞭望塔处设为\(+\infty\)),然后利用倍增计算出二分边界。

然后我们就可以二分了,同时判断第二个条件是否成立。首先,要想抢夺\(a_i\)\(g\)的控制,就必须比\(a_i\)先到达\(g\),那么对于\(g\)有影响的区间就是\([\max(1,g-(u-g)),g]\),对于瞭望塔\(a_j\),满足:

\[l_i+s_i-s_g<l_j+s_g-s_j\\ l_i+s_i-2s_g<l_j-s_j \]

再利用一棵线段树维护区间\(l_i-s_i\)最小值即可。

对于右边界也是同理的。

还有一个关键点,就是如果存在一个点\(j\)满足\(l_i+dis(i,g)=l_j+dis(j,g)\),那么我们需要注意一下,如果\(j\)\(i\)先到达\(g\),我们不必在\(i\)处计算答案,因为在\(j\)处一定会计算到。但是如果\(i,j\)同时到达\(g\),且没有其他点会先抢夺到\(g\),我们就会漏解,因此我们可以在枚举左边界时对这种情况进行特判。

时间复杂度:\(O(n \log^2 n)\)

\(Code:\)

#include<iostream>
#include<cstdio>
#include<algorithm>
#define N 200005
#define ll long long
using namespace std;
const ll INF=1919191919191919;
int n,m,k,w[N],a[N],len[N];
ll ans,s[N],vl[N],gl[N];
struct sgt
{
    ll tr[N << 2];
    void build(int p,int l,int r)
    {
        tr[p]=INF;
        if (l==r)
            return;
        int mid(l+r >> 1);
        build(p << 1,l,mid);
        build(p << 1 | 1,mid+1,r);
    }
    void modify(int p,int l,int r,int x,ll y)
    {
        if (l==r)
        {
            tr[p]=y;
            return;
        }
        int mid(l+r >> 1);
        if (x<=mid)
            modify(p << 1,l,mid,x,y); else
            modify(p << 1 | 1,mid+1,r,x,y);
        tr[p]=min(tr[p << 1],tr[p << 1 | 1]);
    }
    void modify(int x,ll y)
    {
        modify(1,1,n,x,y);
    }
    ll calc(int p,int l,int r,int x,int y)
    {
        if (l==x && r==y)
            return tr[p];
        int mid(l+r >> 1);
        if (y<=mid)
            return calc(p << 1,l,mid,x,y); else
        if (x>mid)
            return calc(p << 1 | 1,mid+1,r,x,y); else
            return min(calc(p << 1,l,mid,x,mid),calc(p << 1 | 1,mid+1,r,mid+1,y));
    }
    ll calc(int x,int y)
    {
        return calc(1,1,n,x,y);
    }
}s1,s2;
int calcL(ll s,int I)
{
    if (!I)
        return 1;
    for (int i=20;i>=0;--i)
        if (I-(1 << i)+1>0 && s1.calc(I-(1 <<i)+1,I)>s)
            I-=(1 << i);
    if (!I || vl[I]<=s)
        ++I;
    return I;
}
int calcR(ll s,int I)
{
    if (I>n)
        return n;
    for (int i=20;i>=0;--i)
        if (I+(1 << i)-1<=n && s2.calc(I,I+(1 << i)-1)>s)
            I+=(1 << i);
    if (I>n || gl[I]<=s)
        --I;
    return I;
}
ll calc1(int l,int r)
{
    return s2.calc(l,r);
}
ll calc2(int l,int r)
{
    return s1.calc(l,r);
}
int main()
{
    scanf("%d%d",&n,&m);
    for (int i=1;i<n;++i)
        scanf("%d",&w[i]),s[i+1]=s[i]+w[i];
    s1.build(1,1,n),s2.build(1,1,n);
    for (int i=1;i<=n;++i)
        vl[i]=gl[i]=INF;
    while (m--)
    {
        scanf("%d",&k);
        for (int i=1;i<=k;++i)
        {
            scanf("%d%d",&a[i],&len[i]);
            vl[a[i]]=s[a[i]]+len[i];
            gl[a[i]]=-s[a[i]]+len[i];
            s1.modify(a[i],vl[a[i]]);
            s2.modify(a[i],gl[a[i]]);
        }
        ans=0;
        for (int i=1;i<=k;++i)
        {
            int u(a[i]);
            int lim(calcL(vl[u],u-1));
            int l(lim),r(u),g(u);
            while (l<=r)
            {
                int mid(l+r >> 1);
                ll L(s[u]-s[mid]+len[i]-s[mid]);
                int pt(max(1,mid-(u-mid)));
                if (calc1(pt,mid)>L)
                    g=mid,r=mid-1; else
                    l=mid+1;
            }
            ans+=u-g+1;
            if (g!=1)
            {
                --g;
                ll L(s[u]-s[g]+len[i]-s[g]);
                int pt(max(1,g-(u-g)));
                if (g-(u-g)>0 && calc1(pt+1,g)>L && gl[pt]==L)
                    ++ans;
            }
        }
        for (int i=1;i<=k;++i)
        {
            int u(a[i]);
            int lim(calcR(gl[u],u+1));
            if (lim==u)
                continue;
            int l(u+1),r(lim),g(-1);
            while (l<=r)
            {
                int mid(l+r >> 1);
                ll L(-s[u]+s[mid]+len[i]+s[mid]);
                int pt(min(n,mid+(mid-u)));
                if (calc2(mid,pt)>L)
                    g=mid,l=mid+1; else
                    r=mid-1;
            }
            if (g==-1)  
                continue;
            ans+=g-u;
        }
        printf("%lld\n",ans);
        for (int i=1;i<=k;++i)
        {
            vl[a[i]]=gl[a[i]]=INF;
            s1.modify(a[i],INF);
            s2.modify(a[i],INF);
        }
    }
    return 0;
}
posted @ 2021-04-01 14:11  GK0328  阅读(40)  评论(0编辑  收藏  举报