To_Heart—题解——[HNOI/AHOI2018]转盘
起因是学长来讲数据结构!讲得很好!于是记录一下以后有机会讲给下一代!!1
题目大意
题意:给定一个 n n n 元环,可以从任意位置出发,每秒可以选择前进或者不动,每个点有一个到达时间 t i t_i ti当到达某个点的时间 T T T 满足 T > t i T>t_i T>ti 时可以标记这个点。问标记所有点的最短的时间。有 m m m 次修改,强制在线。
题解
可以发现如果我们需要停下来等待覆盖一个点,不如将起点向前移。所以可以发现最优的走法一定是没有停留的。
破环成链,发现无论如何一定有 n-1 步,对于每个点作为起点“向前移”的步数是后面每一个到达时间比max,大概就可以写出:
T min = min i ≤ n { max i < j < i + n t j − j + i } + n − 1 T_{\min} = \min _{i\le n} \{ \max _{i<j<i+n} t_j-j+i\} +n - 1 Tmin=i≤nmin{i<j<i+nmaxtj−j+i}+n−1
然后惊奇地发现对于任意的 i i i ,加入 i + n i+n i+n 之后的点参与表达式计算并不会对答案造成影响,所以可以愉快的写出:
T min = min i ≤ n { max i < j ≤ 2 × n t j − j + i } + n − 1 T_{\min} = \min _{i\le n} \{ \max _{i<j \le 2\times n} t_j-j+i\} +n - 1 Tmin=i≤nmin{i<j≤2×nmaxtj−j+i}+n−1
令 a i = t i + i a_i=t_i+i ai=ti+i,则有:
T min = min i ≤ n { max i < j ≤ 2 × n a j + i } + n − 1 T_{\min} = \min _{i\le n} \{ \max _{i<j \le 2\times n} a_j+i\} +n - 1 Tmin=i≤nmin{i<j≤2×nmaxaj+i}+n−1
暴力显然是 O ( n 2 log n ) O(n^2\log n) O(n2logn) 的,但是发现所有的 i 都可以枚举完所有点,这引导我们反过来思考每个点对前面点的贡献。这时候考虑 max 的性质,发现所有可能对答案构成贡献的点的权值是单调递减的,这个序列反过来思考发现是一个从 n 到 1 的极长上升子序列。因为答案要求最小,从后往前看,每个点产生贡献应该尽量提前,所以写出:
T min = m i n { a p j + ( p j − 1 + 1 ) } + n − 1 T_{\min} =min\{ a_{p_j}+(p_{j-1}+1)\} +n -1 Tmin=min{apj+(pj−1+1)}+n−1
其中 p 是找出来的极长上升子序列的下标集合。
然后维护一下极长上升子序列,复杂度为 O ( n log n 2 ) O(n \log n^2 ) O(nlogn2)
#include<bits/stdc++.h>
using namespace std;
int n,m,p;
struct zz{
int l,r;
int mx;
int len;
};
int a[2000005];
int t[2000005];
struct Tree{
#define lc p<<1
#define rc p<<1|1
zz t[20000005];
void Build_Tree(int p,int l,int r){
t[p].l=l,t[p].r=r;
if(t[p].l==t[p].r) return t[p].mx=a[l],t[p].len=::t[l],void();
int mid=(l+r)>>1;
Build_Tree(lc,l,mid),Build_Tree(rc,mid+1,r);
t[p].mx=max(t[lc].mx,t[rc].mx);
t[p].len=Push_Up(lc,t[rc].mx);
}
int Push_Up(int p,int val){
if(t[p].l==t[p].r) return t[p].l+max(t[p].mx,val);
int mid=(t[p].l+t[p].r)>>1;
if(t[rc].mx>=val) return min(t[p].len,Push_Up(rc,val));
else return min(Push_Up(lc,val),mid+1+val);
}
void Change_Tree(int p,int l){
if(t[p].l==t[p].r) return t[p].mx=a[l],t[p].len=::t[l],void();
int mid=(t[p].l+t[p].r)>>1;
if(l<=mid) Change_Tree(lc,l);
else Change_Tree(rc,l);
t[p].mx=max(t[lc].mx,t[rc].mx);
t[p].len=Push_Up(lc,t[rc].mx);
}
}T;
int main(){
cin>>n>>m>>p;
for(int i=1;i<=n;i++) scanf("%d",&t[i]),a[i]=t[i]-i,t[i+n]=t[i],a[i+n]=t[i+n]-(i+n);
T.Build_Tree(1,1,n<<1);
int ans=0;
printf("%d\n",ans=T.t[1].len+n-1);
for(int i=1,x,y;i<=m;i++){
scanf("%d%d",&x,&y);
if(p) x^=ans,y^=ans;
t[x]=t[x+n]=y,a[x]=y-x,a[x+n]=y-x-n;
T.Change_Tree(1,x),T.Change_Tree(1,x+n);
printf("%d\n",ans=T.t[1].len+n-1);
}
return 0;
}