[洛谷P4425][BZOJ5286][HNOI/AHOI2018]转盘

Solution

  • 如果一种最优解是:在时刻 t t t从位置 x x x开始,从时刻 t + 1 t+1 t+1开始共有 t 1 t1 t1个时刻选择当前物品,有 t 2 t2 t2个时刻选择下一个物品,那么这和在时刻 t + t 1 t+t1 t+t1从位置 x x x开始,从时刻 t + 1 t+1 t+1开始都不选择当前物品,实际上是等价的
  • 那么只要考虑不停留的情况,问题转化为选择一个最小的开始时间 b x ( t ≥ 0 ) b_{x}(t≥0) bx(t0),使得从位置 x x x开始,花 n n n个时刻完成标记,答案即 t + n − 1 t+n-1 t+n1
  • 首先,为了处理环的情况,令 T T T数组倍长,即 T i = T i − n ( n + 1 ≤ i ≤ 2 n ) T_{i}=T_{i-n}(n+1≤i≤2n) Ti=Tin(n+1i2n)
  • 因为要求满足 t + i − x ≥ T i ( ∀ i , x ≤ i ≤ x + n − 1 ) t+i-x≥T_{i}(\forall i,x≤i≤x+n-1) t+ixTi(ixix+n1)
  • 所以 b x bx bx m a x i = x x + n − 1 T i − i max_{i=x}^{x+n-1}T_{i}-i maxi=xx+n1Tii
  • 那么令 a i = T i − i a_{i}=T_{i}-i ai=Tii
  • 发现 T i = T i − n ( x + n ≤ i ≤ 2 n ) T_{i}=T_{i-n}(x+n≤i≤2n) Ti=Tin(x+ni2n)
  • 说明 a i &lt; a i − n ( x + n ≤ i ≤ 2 n ) a_{i}&lt;a_{i-n}(x+n≤i≤2n) ai<ain(x+ni2n)
  • 那么 b x b_{x} bx可以改写为后缀最大值的形式: m a x i = x 2 n a i ( 1 ≤ x ≤ n ) max_{i=x}^{2n}a_{i}(1≤x≤n) maxi=x2nai(1xn)
  • 因为 a n s = m i n ( b x + x ) ans=min(b_{x}+x) ans=min(bx+x)
  • 所以如果对于 l ≤ i ≤ r l≤i≤r lir满足 b i b_{i} bi全部相同,那么选 l l l一定比选 [ l + 1 , r ] [l+1,r] [l+1,r]中的任何一个要优
  • 将所有这样区间 [ l , r ] [l,r] [l,r]中的 l l l取出,就形成了一个关于 a i a_{i} ai的单调下降子序列
  • 特别地,这个子序列的每一项 d i d_{i} di都要满足 a j &lt; a i ( ∀ j , i &lt; j ≤ 2 n ) a_{j}&lt;a_{i}(\forall j,i&lt;j≤2n) aj<ai(ji<j2n)
  • 其实只要最后一项满足上述条件即可,也就是对这个单调下降子序列的末项作了限制
  • 那么为了方便,从右到左维护一个单调上升子序列,也就是取出所有的 r r r放入 c c c数组,所有的 c i + 1 c_{i}+1 ci+1就是要选的 l l l,对答案的贡献即 ( 1 ) . m i n ( a c i + 1 + c i + 1 ) ( 1 ≤ i &lt; m ) (1).min(a_{c_{i+1}}+c_{i}+1)(1≤i&lt;m) (1).min(aci+1+ci+1)(1i<m)
  • 特别地,由于 x x x c c c的取值在 [ 1 , n ] [1,n] [1,n],所以当 c m ! = n c_{m}!=n cm!=n时,位置 c m + 1 c_{m}+1 cm+1对答案的贡献是 ( 2 ) . m a x i = n + 1 2 n a i (2).max_{i=n+1}^{2n}a_{i} (2).maxi=n+12nai
  • m a x i = 1 n a i − n max_{i=1}^{n}a_{i}-n maxi=1nain
  • 因为在此条件下, c m + 1 , n , n + 1 c_{m}+1,n,n+1 cm+1,n,n+1必处于同一区间 [ l , r ] [l,r] [l,r]
  • c m = n c_{m}=n cm=n ( 2 ) (2) (2)显然成立
  • 于是对 ( 1 ) , ( 2 ) , a c 1 + 1 (1),(2),a_{c_{1}}+1 (1),(2),ac1+1取最小值即可
  • 维护线段树,记 c a l c ( l , r , v , p ) calc(l,r,v,p) calc(l,r,v,p)为一个四元组,表示节点 p p p(对应区间 [ l , r ] [l,r] [l,r]),末项 &gt; v &gt;v >v的单调上升子序列(从右到左)的信息: ( c 1 , c m , a c 1 , m i n ( a c i + 1 + c i + 1 ) ( 1 ≤ i &lt; m ) ) (c_{1},c_{m},a_{c_{1}},min(a_{c_{i+1}}+c_{i}+1)(1≤i&lt;m)) c1,cm,ac1,min(aci+1+ci+1)(1i<m)
  • v a l [ p ] val[p] val[p]表示节点 p p p对应区间的 m a x ( a i ) max(a_{i}) max(ai)
  • b [ p ] b[p] b[p]表示 c a l c ( l , m i d , v a l [ p 3 ] , p 2 ) calc(l,mid,val[p3],p2) calc(l,mid,val[p3],p2) p 2 , p 3 p2,p3 p2,p3为左右子节点
  • 修改回溯时要更新 v a l val val b b b
  • 下面讨论如何计算 c a l c ( l , r , v , p ) calc(l,r,v,p) calc(l,r,v,p)
  • v a l [ p 3 ] ≤ v val[p3]≤v val[p3]v,那么这个子序列与 p 3 p3 p3无关,递归 p 2 p2 p2即可
  • 否则, p 2 p2 p2对子序列的贡献依然是 b [ p ] b[p] b[p]
  • 显然回溯时 b [ p 2 ] b[p2] b[p2]会比 c a l c ( l , m i d , v a l [ p 3 ] , p 2 ) calc(l,mid,val[p3],p2) calc(l,mid,val[p3],p2)先算
  • 时间复杂度 O ( n l o g 2 n ) O(nlog^2n) O(nlog2n)

Code

#include <bits/stdc++.h>

using namespace std;

#define p2 p << 1
#define p3 p << 1 | 1

template <class t>
inline void read(t & res)
{
	char ch;
	while (ch = getchar(), !isdigit(ch));
	res = ch ^ 48;
	while (ch = getchar(), isdigit(ch))
	res = res * 10 + (ch ^ 48);
}

const int e = 1e5 + 5, inf = 0x3f3f3f3f;
struct node
{
	int c1, cm, a1, res;
}b[e * 4];
int n, m, val[e * 4], a[e], op, ans;

inline node calc(int l, int r, int v, int p)
{
	if (l == r) return val[p] > v ? (node){l, r, val[p], inf} : (node){0, inf, inf, inf};
	int mid = l + r >> 1;
	if (val[p3] <= v) return calc(l, mid, v, p2);
	if (val[p2] <= val[p3]) return calc(mid + 1, r, v, p3);
	node lc = b[p], rc = calc(mid + 1, r, v, p3);
	if (!lc.c1) return rc;
	return (node){lc.c1, rc.cm, lc.a1, min(min(lc.res, rc.res), rc.a1 + lc.cm + 1)};
}

inline void update(int l, int r, int s, int v, int p)
{
	if (l == r)
	{
		val[p] = v;
		return;
	}
	int mid = l + r >> 1;
	if (s <= mid) update(l, mid, s, v, p2);
	else update(mid + 1, r, s, v, p3);
	val[p] = max(val[p2], val[p3]);
	b[p] = calc(l, mid, val[p3], p2);
}

inline void build(int l, int r, int p)
{
	if (l == r)
	{
		val[p] = a[l] - l;
		return;
	}
	int mid = l + r >> 1;
	build(l, mid, p2);
	build(mid + 1, r, p3);
	val[p] = max(val[p2], val[p3]);
	b[p] = calc(l, mid, val[p3], p2);
}

int main()
{
	int i, x, y;
	read(n); read(m); read(op);
	for (i = 1; i <= n; i++) read(a[i]);
	build(1, n, 1);
	node z = calc(1, n, val[1] - n, 1);
	ans = min(z.res, min(z.a1 + 1, val[1] - n + z.cm + 1));
	ans += n - 1;
	printf("%d\n", ans);
	while (m--)
	{
		read(x);
		read(y);
		if (op)
		{
			x ^= ans;
			y ^= ans;
		}
		a[x] = y;
		update(1, n, x, y - x, 1);
		node z = calc(1, n, val[1] - n, 1);
		ans = min(z.res, min(z.a1 + 1, val[1] - n + z.cm + 1));
		ans += n - 1;
		printf("%d\n", ans);
	}
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值