zoukankan      html  css  js  c++  java
  • 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 eq 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
    ",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
    ",lastans);
        }
        return 0;
    }
  • 相关阅读:
    POJ-1502-MPI Maelstrom
    POJ-3259-Wormholes
    【BZOJ4399】—膜法少女LJJ(线段树合并)
    省选模板复习—【计算几何】
    【BZOJ2115】【WC2011】—Xor(线性基)
    【洛谷P5290】【十二省联考2019】春节十二响(贪心+启发式合并)
    【BZOJ5461】 【PKUWC2018】—Minimax(线段树合并优化dp)
    【LOJ#3043】【洛谷P5280】【ZJOI2019】—线段树(计数dp+线段树)
    【省选模拟】—猎人杀(概率dp)
    【BZOJ4828】【HNOI2017】—大佬(LmyAKIOI!)
  • 原文地址:https://www.cnblogs.com/zhangleo/p/10901763.html
Copyright © 2011-2022 走看看