bzoj 5286

极其神的一道题

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

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

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

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

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

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

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

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

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

设当前是第i号点,那么要等的最小时间即为maxj=ii+n1(Tjj+i+1)

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

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

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

现在假设这些你都会了

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

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

vi=Tii,那么原式即化成maxj=ii+n1(vj+i+1)

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

这是个好问题,怎么找?

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

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

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

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

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

复制代码
#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 @   lleozhang  Views(172)  Comments(1Edit  收藏  举报
编辑推荐:
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
阅读排行:
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
levels of contents
点击右上角即可分享
微信分享提示