problem
洛谷链接
solution
结论
:最优方案中一定有一种是全程不停的。
断环成链,接一个 [1,n][1,n][1,n] 在后面形成 2n2n2n 的序列,同时将时间戳逆过来。
转化成:在 ttt 时刻从某个位置 i∈[n,2n)i\in[n,2n)i∈[n,2n) 开始往前取物品,时刻 −1-1−1,物品 jjj 在 tjt_jtj 时刻消失,要求标记所有的物品。最小化这个 ttt。
ttt 时刻开始从 iii 位置到物品 jjj,需要 i−ji-ji−j 的时间,即 ∀j∈(i−n,i]t−(i−j)≥Tj\forall_{j\in(i-n,i]}\ t-(i-j)\ge T_j∀j∈(i−n,i] t−(i−j)≥Tj。
显然全程不停歇是更好的。
t−(i−j)≥tj⇒t≥tj−j+i,j∈(i−n,i]⇒tmin=max{tj−j}+i,j∈(i−n,i]t-(i-j)\ge t_j\Rightarrow t\ge t_j-j+i,j\in(i-n,i]\Rightarrow t_{\min}=\max\{t_j-j\}+i,j\in(i-n,i]t−(i−j)≥tj⇒t≥tj−j+i,j∈(i−n,i]⇒tmin=max{tj−j}+i,j∈(i−n,i]。
考虑枚举 i∈[n,2n)i\in[n,2n)i∈[n,2n),算出 [i]tmin[i]t_{\min}[i]tmin 整体取 min\minmin 才是最终答案,每个 iii 的计算都要 nlognn\log nnlogn。
令 ai=Ti−ia_i=T_i-iai=Ti−i,则 ans=minn≤i<2n{maxi−n<j≤i{aj}+i}ans=\min_{n\le i< 2n}\big\{\max_{i-n<j\le i}\{a_j\}+i\big\}ans=minn≤i<2n{maxi−n<j≤i{aj}+i}。
用 iii 替换 i+n−1i+n-1i+n−1,有 ans=min1≤i≤n{maxi≤j≤i+n−1{aj}+i}+n−1ans=\min_{1\le i\le n}\big\{\max_{i\le j\le i+n-1}\{a_j\}+i\big\}+n-1ans=min1≤i≤n{maxi≤j≤i+n−1{aj}+i}+n−1。
因为 ai=Ti−i>ai+n=Ti−(i+n)a_i=T_i-i>a_{i+n}=T_{i}-(i+n)ai=Ti−i>ai+n=Ti−(i+n),所以稍微扩大 jjj 范围对答案无影响。
即 ans=min1≤i≤n{maxi≤j≤2n{aj}+i}+n−1ans=\min_{1\le i\le n}\big\{\max_{i\le j\le 2n}\{a_j\}+i\big\}+n-1ans=min1≤i≤n{maxi≤j≤2n{aj}+i}+n−1。
对 iii 处理一个后缀最大值就能得到答案,到此也只是扔了个 logloglog 并没起到多大优化,O(nm)O(nm)O(nm) 仍无法接受。
iii 不行转过来考虑 jjj,考虑每个 jjj 的答案是怎样的。(特别地,a0=infa_0=infa0=inf)
-
如果 jjj 是后缀最大值点。
对于 jjj,往前找到第一个 ak>aja_k>a_jak>aj 的 kkk,i∈(k,n]i\in(k,n]i∈(k,n] 都是 jjj 处取 max\maxmax,当 i=k+1i=k+1i=k+1 时才满足外层的 min\minmin 操作,因此 jjj 的贡献应为 aj+k+1a_j+k+1aj+k+1。
由 iii 的限制可以反推得到 k<nk<nk<n。
-
如果 jjj 不是,那答案仍是后面某个点产生的。
-
最后就是这些 jjj 贡献取 min\minmin。
基于这样的分类,发现可以从后往前维护 aja_jaj 的一个单调递增栈。
假设单调栈内的元素为 s0=0<s1<...<sxs_0=0<s_1<...<s_xs0=0<s1<...<sx。
ans=min1≤i≤x{asi+si−1+1}+n−1=min1≤i≤x{asi+si−1}+nans=\min_{1\le i\le x}\big\{a_{s_i}+s_{i-1}+1\big\}+n-1=\min_{1\le i\le x}\big\{a_{s_i}+s_{i-1}\big\}+nans=min1≤i≤x{asi+si−1+1}+n−1=min1≤i≤x{asi+si−1}+n,要求 si−1<ns_{i-1}<nsi−1<n。
用线段树来维护单调栈楼房重建,具体合并两个区间单调栈。
- 右子树答案直接拿。
- 用右子树最大值在做子树内找到最靠近右子树的第一个比其大的位置返回。
- 对该位置前面的取整体最小值。
最优位置 jjj 要满足 sj−1<ns_{j-1}<nsj−1<n 所以要记录这个询问的结果位置。
但最终答案实际上就是 i∈(n,2n]i\in(n,2n]i∈(n,2n] 的最大值在 [1,n][1,n][1,n] 里面二分找 jjj 执行上述流程中得到的。
看似是维护 ai(1≤i≤2n)a_i(1\le i\le 2n)ai(1≤i≤2n) 的最大值,事实上 i∈[n,2n)i\in[n,2n)i∈[n,2n) 的最大值 ai=Ti−ia_i=T_i-iai=Ti−i,就等于 i−n∈[1,n)i-n\in[1,n)i−n∈[1,n) 的最大值 ai−n=Ti−n−(i−n)=Ti−i+na_{i-n}=T_{i-n}-(i-n)=T_i-i+nai−n=Ti−n−(i−n)=Ti−i+n 减去 nnn。
所以线段树只需要维护 [1,n][1,n][1,n] 的最大值信息即可,再在 [1,n][1,n][1,n] 里面查。
时间复杂度 O((n+Q)logn2)O((n+Q)\log n^2)O((n+Q)logn2)。
code
#include <bits/stdc++.h>
using namespace std;
#define inf 0x7f7f7f7f
#define maxn 100005
int n, m, p;
int t[maxn], Max[maxn << 2], Ans[maxn << 2];#define lson now << 1
#define rson now << 1 | 1
#define mid (l + r >> 1)int query( int now, int l, int r, int x ) {if( l == r ) return Max[now] > x ? x + l : inf;if( Max[rson] <= x ) return query( lson, l, mid, x );else return min( Ans[now], query( rson, mid + 1, r, x ) );
}void pushup( int now, int l, int r ) {Max[now] = max( Max[lson], Max[rson] );Ans[now] = query( lson, l, mid, Max[rson] );
}void build( int now, int l, int r ) {if( l == r ) { Max[now] = t[l] - l; return; }build( lson, l, mid );build( rson, mid + 1, r );pushup( now, l, r );
}void modify( int now, int l, int r, int pos ) {if( l == r ) { Max[now] = t[pos] - pos; return; }if( pos <= mid ) modify( lson, l, mid, pos );else modify( rson, mid + 1, r, pos );pushup( now, l, r );
}int main() {scanf( "%d %d %d", &n, &m, &p );for( int i = 1;i <= n;i ++ ) scanf( "%d", &t[i] );build( 1, 1, n );int ans = query( 1, 1, n, Max[1] - n ) + n;printf( "%d\n", ans );while( m -- ) {int x, y;scanf( "%d %d", &x, &y );if( p ) x ^= ans, y ^= ans;t[x] = y, modify( 1, 1, n, x );ans = query( 1, 1, n, Max[1] - n ) + n;printf( "%d\n", ans );}return 0;
}