bzoj 5286

极其神的一道题

首先看到:环上问题并不好做,所以我们按套路拆环之后扔到序列上

接下来,我们来分析一下这个游戏的最优策略

最优策略一般有两种表述,这里一并给出

①:在原地等到某一个时间点从某一个起点开始走,不停留地恰好走完一圈达到结束,取所有可行方案中最小值

②:从某一个点为起点开始走,对每个点如果没到时间就等到时间往下走,走完一圈结束,取所有可行方案中最小值

这两个表述可以证明是等价的(你证一个)因此我们采用第一种表述(主要是为了推式子方便)

首先无论从哪一个点开始走,我们都保证只走一圈,因此走圈的代价是固定的$n-1$

于是问题被转化成了求从某一个点开始要等的最小时间

那么我们把这个最小时间的表达式列出来:

设当前是第$i$号点,那么要等的最小时间即为$max_{j=i}^{i+n-1}(T_j-j+i+1)$

于是我们只需求出这个最大值即可

每求一次是$O(n)$的,时间复杂度不够优秀,因此我们需要考虑数据结构维护

这里需要一点前置知识,如果你没有做过bzoj 2957的话应该先做一下这道题或者至少看一下这里

现在假设这些你都会了

接下来进行一些神奇的变化:

不难发现,$T_j-j$这个表达式与$i$无关,因此我们考虑维护他

设$v_i=T_i-i$,那么原式即化成$max_{j=i}^{i+n-1}(v_j+i+1)$

那么,对于每一个确定的$j$,我们只需找出一个最小的$i$与之对应即可

这是个好问题,怎么找?

首先我们要注意到,这里的值是要求取最大的,同时要求$j\geq i$,因此我们需要维护的是后缀最大值

那么,借助那道题的思想,我们在每次维护时仍然分左右区间计算,只不过这次是右区间影响左区间

我们用右区间的最大值作为限制,要求左区间中选取最大值大于右区间者进行更新,最后注意算出的是代价,取最小值即可

什么?为什么右区间的贡献不继承上来?

因为右半部分是我复制出来的,起点要求在左半部分里

#include <cstdio>
#include <cmath>
#include <cstring>
#include <cstdlib>
#include <iostream>
#include <algorithm>
#include <queue>
#include <stack>
#define rt1 rt<<1
#define rt2 (rt<<1)|1
using namespace std;
const int inf=0x3f3f3f3f;
struct Seg_tree
{
    int maxh;
    int maxlen;
}tree[800005];
int a[200005];
int n,m,p;
int pushup(int rt,int l,int r,int lim)
{
    if(l==r)
    {
        if(tree[rt].maxh<=lim)return inf;
        return lim+l+1;
    }
    int mid=(l+r)>>1;
    if(tree[rt2].maxh<=lim)return pushup(rt1,l,mid,lim);
    return min(tree[rt].maxlen,pushup(rt2,mid+1,r,lim));
}
void buildtree(int rt,int l,int r)
{
    if(l==r)
    {
        tree[rt].maxh=a[l]-l;
        return;
    }
    int mid=(l+r)>>1;
    buildtree(rt1,l,mid),buildtree(rt2,mid+1,r);
    tree[rt].maxh=max(tree[rt1].maxh,tree[rt2].maxh);
    tree[rt].maxlen=pushup(rt1,l,mid,tree[rt2].maxh);
}
void update(int rt,int l,int r,int posi)
{
    if(l==r)
    {
        tree[rt].maxh=a[l]-l;
        return;
    }
    int mid=(l+r)>>1;
    if(posi<=mid)update(rt1,l,mid,posi);
    else update(rt2,mid+1,r,posi);
    tree[rt].maxh=max(tree[rt1].maxh,tree[rt2].maxh);
    tree[rt].maxlen=pushup(rt1,l,mid,tree[rt2].maxh);
}
int main()
{
    scanf("%d%d%d",&n,&m,&p);
    for(int i=1;i<=n;i++)scanf("%d",&a[i]),a[i+n]=a[i];
    n<<=1;
    buildtree(1,1,n);
    int lastans=min(tree[1].maxlen,tree[1].maxh+1)+n/2-1;
    printf("%d\n",lastans);
    for(int i=1;i<=m;i++)
    {
        int x,y;
        scanf("%d%d",&x,&y);
        if(p)x^=lastans,y^=lastans;
        a[x]=y,a[x+n/2]=y;
        update(1,1,n,x),update(1,1,n,x+n/2);
        lastans=tree[1].maxlen+n/2-1;
        printf("%d\n",lastans);
    }
    return 0;
}

 

posted @ 2019-05-21 19:35  lleozhang  Views(171)  Comments(1Edit  收藏  举报
levels of contents