題解 洛谷 P4425 【[HNOI/AHOI2018]轉盤】
阿新 • • 發佈:2020-08-06
發現最優解可以表示為在起點等待一段時間,然後不停頓地走完一圈。因為停頓的原因是當前的物品沒有出現,所以可以在起點先等待,然後不停頓地來標記。
對環倍長來將其轉化為鏈,用 \(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]\)
具體地,若 \(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; }