1. 程式人生 > 實用技巧 >題解 洛谷 P4425 【[HNOI/AHOI2018]轉盤】

題解 洛谷 P4425 【[HNOI/AHOI2018]轉盤】

發現最優解可以表示為在起點等待一段時間,然後不停頓地走完一圈。因為停頓的原因是當前的物品沒有出現,所以可以在起點先等待,然後不停頓地來標記。

對環倍長來將其轉化為鏈,用 \(i\) 來列舉起始位置,\(j\) 來列舉物品,則答案可表示為:

\[\min\limits_{i=1}^n\left \{ i+\max\limits_{j=i}^{2n} \left \{t_j-j \right \} \right \} +n-1 \]

這裡可以將 \(\max\) 的列舉範圍擴大,因為擴大的部分不可能成為最大值,所以不影響答案。

考慮用線段樹來維護答案,因為倍長後長度為 \(2n\),起始位置只可能為 \([1,n]\)

,所以對於一個區間,只維護左區間的答案即可。線段樹上維護當前區間的答案和 \(t_i-i\) 的最大值,合併兩個區間時,用右區間的最大值 \(v\) 在左區間二分即可。

具體地,若 \(v\) 比當前區間右區間的最大值大,則說明 \(mid\) 往右的最大值是 \(v\),右區間就直接返回左端點的答案即可,若 \(v\) 更小,就不用再進入左區間,區間返回當前區間的答案即可。

線段樹根節點的答案即為所求。維護方法和樓房重建很像。

\(code:\)

#include<bits/stdc++.h>
#define maxn 800010
#define ls (cur<<1)
#define rs (cur<<1|1)
#define mid ((l+r)>>1)
using namespace std;
template<typename T> inline void read(T &x)
{
    x=0;char c=getchar();bool flag=false;
    while(!isdigit(c)){if(c=='-')flag=true;c=getchar();}
    while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
    if(flag)x=-x;
}
int n,m,ans,p,root=1;
int t[maxn],ma[maxn],val[maxn];
int query(int l,int r,int v,int cur)
{
    if(l==r) return l+max(v,ma[cur]);
    if(v>=ma[rs]) return min(mid+1+v,query(l,mid,v,ls));
    return min(val[cur],query(mid+1,r,v,rs));
}
void pushup(int cur,int l,int r)
{
    ma[cur]=max(ma[ls],ma[rs]);
    val[cur]=query(l,mid,ma[rs],ls);
}
void build(int l,int r,int cur)
{
    if(l==r)
    {
        val[cur]=t[l],ma[cur]=t[l]-l;
        return;
    }
    build(l,mid,ls),build(mid+1,r,rs),pushup(cur,l,r);
}
void modify(int l,int r,int pos,int v,int cur)
{
    if(l==r)
    {
        val[cur]=v,ma[cur]=v-l;
        return;
    }
    if(pos<=mid) modify(l,mid,pos,v,ls);
    else modify(mid+1,r,pos,v,rs);
    pushup(cur,l,r);
}
int main()
{
    read(n),read(m),read(p);
    for(int i=1;i<=n;++i) read(t[i]),t[i+n]=t[i];
    build(1,2*n,root),printf("%d\n",ans=val[root]+n-1);
    while(m--)
    {
        int x,y;
        read(x),read(y),x^=ans*p,y^=ans*p;
        modify(1,2*n,x,y,root),modify(1,2*n,x+n,y,root);
        printf("%d\n",ans=val[root]+n-1);
    }
    return 0;
}