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; }