zoukankan      html  css  js  c++  java
  • [洛谷P4425][BZOJ5286][HNOI/AHOI2018]转盘(线段树)

    Solution

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

    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;
    }
    
  • 相关阅读:
    NSArray & NSDictionary
    copy&mutableCopy 浅拷贝(shallow copy)深拷贝 (deep copy)
    03-图形上下文栈, 图形的平移 旋转 缩放
    02- 画文字和图片-------------之前写的那个微博项目,可以试试用画图片的方式来处理,这样应该比UILabel 代码少点,一会试试
    Quartz 官网翻译(转载)
    01 画简单线的方法
    @property 修饰符
    SEL 类型
    Java 常用快捷键
    Java判断是否为数字
  • 原文地址:https://www.cnblogs.com/cyf32768/p/12196671.html
Copyright © 2011-2022 走看看