1. 程式人生 > >BZOJ5286 HNOI/AHOI2018轉盤(分塊/線段樹)

BZOJ5286 HNOI/AHOI2018轉盤(分塊/線段樹)

使用 lib 最優 其中 最小 ring tchar bzoj spa

  顯然最優走法是先一直停在初始位置然後一次性走完一圈。將序列倍長後,相當於找一個長度為n的區間[l,l+n),使其中ti+l+n-1-i的最大值最小。容易發現ti-i>ti+n-(i+n),所以也就相當於是後綴最大值最小。設ti-i=ai,即要求min{l+max{al..2n}}+n-1 (l=1..n)。如果沒有修改的話只要掃一遍就行了。

  線段樹看起來很難維護,考慮分塊。每一塊求出僅考慮該塊的ai時上述值的前綴min和ai的後綴max。對於查詢,從後往前考慮所選區間左端點在哪一塊。如果該塊某個位置出現了整個序列的後綴最大值,序列後面的部分就不會對該塊之前位置的答案產生影響,可以直接使用之前求出的答案。於是根據後綴最大值將該塊劃分成兩部分,後一部分由於max{ai

}被固定為後綴最大值,當然選擇盡量左的點時最優。修改時暴力重構塊即可。塊大小取nsqrt(nlogn)時最優,為O(msqrt(nlogn))。沒有卡常也在慢如狗的bzoj上只跑了11s。

  事實上這個做法可以擴展到線段樹上。待補。

#include<iostream> 
#include<cstdio>
#include<cmath>
#include<cstdlib>
#include<cstring>
#include<algorithm>
using namespace std;
#define ll long long
#define
N 200010 #define inf 2000000010 char getc(){char c=getchar();while ((c<A||c>Z)&&(c<a||c>z)&&(c<0||c>9)) c=getchar();return c;} int gcd(int n,int m){return m==0?n:gcd(m,n%m);} int read() { int x=0,f=1;char c=getchar(); while (c<0||c>9) {if (c==-) f=-1
;c=getchar();} while (c>=0&&c<=9) x=(x<<1)+(x<<3)+(c^48),c=getchar(); return x*f; } int n,m,op,a[N],L[N],R[N],pos[N],mx[N],mn[N],block,num,ans=inf; void build(int x) { mx[R[x]]=a[R[x]];mn[R[x]]=R[x]+a[R[x]]; for (int i=R[x]-1;i>=L[x];i--) mn[i]=i+(mx[i]=max(mx[i+1],a[i])); for (int i=L[x]+1;i<=R[x];i++) mn[i]=min(mn[i-1],mn[i]); } int query() { int u=-inf,ans=inf; for (int i=2*num;i>num;i--) u=max(u,mx[L[i]]); for (int i=num;i>=1;i--) { int l=L[i],r=R[i],x=R[i]+1; while (l<=r) { int mid=l+r>>1; if (mx[mid]<=u) x=mid,r=mid-1; else l=mid+1; } ans=min(ans,x+u);if (x>L[i]) ans=min(ans,mn[x-1]); u=max(u,mx[L[i]]); } return ans; } int main() { #ifndef ONLINE_JUDGE freopen("bzoj5286.in","r",stdin); freopen("bzoj5286.out","w",stdout); const char LL[]="%I64d\n"; #else const char LL[]="%lld\n"; #endif n=read(),m=read(),op=read();block=sqrt(n*log(n+1));num=(n-1)/block+1; for (int i=1;i<=n;i++) a[i]=a[i+n]=read(); for (int i=1;i<=n*2;i++) a[i]-=i; for (int i=1;i<=num;i++) { L[i]=R[i-1]+1,R[i]=min(n,L[i]+block-1); for (int j=L[i];j<=R[i];j++) pos[j]=i; build(i); } for (int i=num+1;i<=2*num;i++) { L[i]=R[i-1]+1,R[i]=min(2*n,L[i]+block-1); for (int j=L[i];j<=R[i];j++) pos[j]=i; build(i); } ans=query()+n-1;cout<<ans<<endl; while (m--) { int x=read()^ans*op,y=read()^ans*op; a[x]=y-x,a[x+n]=y-x-n;build(pos[x]),build(pos[x+n]); printf("%d\n",ans=query()+n-1); } return 0; }

BZOJ5286 HNOI/AHOI2018轉盤(分塊/線段樹)