• Codeforces 765F 【Souvenirs】


    Description

    传送门


    Solution

    首先容易想到要把询问离线掉,按照套路枚举右端点(r),(ans_i)表示([i, r])的答案。

    这样我们只需要考虑每次加进来一个点(a_r)对所有答案的贡献。

    (a_r)将点分为两种:权值大于它的和小于它的。

    因为两种是对称的所以只讨论权值大于它的。

    先找到最靠右的大于它的点(a_x),这个点可能不是任何区间的最优答案,但是它能更新到的区间是最多的,即左端点为(1 dots x),右端点为(r)的所有区间。

    这样再在(x)左边找可以更新答案的点(a_z),它能更新到的区间是左端点为(1 dots z),右端点为(r)的所有区间,也就是说它能更新到的区间是被(a_x)能更新的区间完全包含的。所以如果新的点的权值比(a_x)还大,肯定不会更优,也就是说(a_z)必须小于(a_x)

    但是这样还是不行,严格递增序列会把这样的过程卡到单次(O(n))

    考虑之前以右端点枚举到(x)的时候,(a_z)能更新的所有区间已经被(a_x - a_z)更新过了,那么如果现在的(a_z - a_r)(a_x - a_z)更大,就没有任何用处了。

    化简这个式子,有

    [a_z < frac{a_x + a_r}{2} ]

    这样枚举右端点之后每次只需要找(log(n))次就行了,每次查找的复杂度是(o(logn))的,那么总复杂度是(O(n log^2 n))

    每次要用某个权值去更新一段前缀的答案,直接吉司机线段树显然非常不行。

    所以考虑只对某个点更新,查询的时候查一个后缀最小值,用树状数组实现即可。


    Code

    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    #include <cmath>
    
    using namespace std;
    
    const int N = 100000;
    const int INF = 0x7f7f7f7f;
    
    int n, a[N + 50], root[N + 50], tot, cwq, b[N + 50], q, pos;
    
    struct Ask
    {
    	int l, r, id, ans;
    } ask[N * 3 + 50];
    
    struct Bit
    {
    	int minn[N + 50];
    	void Pre()
    	{
    		memset(minn, 0x7f, sizeof(minn));
    		return;
    	}
    	int Lowbit(int x)
    	{
    		return x & -x;
    	}
    	void Update(int pos, int v)
    	{
    		for (int i = pos; i <= n; i += Lowbit(i))
    			minn[i] = min(minn[i], v);
    		return;
    	}
    	int Query(int pos)
    	{
    		int minnn = INF;
    		for (int i = pos; i; i -= Lowbit(i)) minnn = min(minnn, minn[i]);
    		return minnn;
    	}
    } bit;
    
    struct SegmentTree
    {
    	int s[(N << 2) + 50];
    	void Update(int k, int l, int r, int pos, int zhi)
    	{
    		if (l == r)
    		{
    			s[k] = zhi;
    			return;
    		}
    		int mid = (l + r) >> 1;
    		if (pos <= mid) Update(k << 1, l, mid, pos, zhi);
    		else Update(k << 1 | 1, mid + 1, r, pos, zhi);
    		s[k] = max(s[k << 1], s[k << 1 | 1]);
    		return;
    	}
    	int Query(int k, int l, int r, int x, int y)
    	{
    		if (x > y) return 0;
    		if (x <= l && r <= y) return s[k];
    		int mid = (l + r) >> 1;
    		if (y <= mid) return Query(k << 1, l, mid, x, y);
    		else if (x > mid) return Query(k << 1 | 1, mid + 1, r, x, y);
    		else return max(Query(k << 1, l, mid, x, y), Query(k << 1 | 1, mid + 1, r, x, y));
    	}
    } tr;
    
    void Read(int &x)
    {
    	x = 0; int p = 0; char st = getchar();
    	while (st < '0' || st > '9') p = (st == '-'), st = getchar();
    	while (st >= '0' && st <= '9') x = (x << 1) + (x << 3) + st - '0', st = getchar();
    	x = p ? -x : x;
    	return;
    }
    
    void Print(int x)
    {
    	if (x > 9) Print(x / 10);
    	putchar(x % 10 + '0');
    	return;
    }
    
    int Cmp(Ask a, Ask b)
    {
    	return a.r < b.r;
    }
    
    int Cmp1(Ask a, Ask b)
    {
    	return a.id < b.id;
    }
    
    int Abs(int x)
    {
    	return x < 0 ? -x : x;
    }
    
    int Find(int x)
    {
    	int l = 0, r = cwq;
    	while (l < r)
    	{
    		int mid = (l + r + 1) >> 1;
    		if (b[mid] <= x) l = mid;
    		else r = mid - 1;
    	} 
    	return l;
    }
    
    int main()
    {
    	Read(n);
    	for (int i = 1; i <= n; i++) Read(a[i]), b[++cwq] = a[i];
    	sort(b + 1, b + cwq + 1);
    	cwq = unique(b + 1, b + cwq + 1) - b - 1;
    	for (int i = 1; i <= n; i++) a[i] = lower_bound(b + 1, b + cwq + 1, a[i]) - b;
    	Read(q);
    	for (int i = 1; i <= q; i++) Read(ask[i].l), Read(ask[i].r), ask[i].id = i;
    	sort(ask + 1, ask + q + 1, Cmp);
    	pos = 1;
    	bit.Pre();
    	for (int i = 1; i <= n; i++)
    	{
    		if (i != 1) 
    		{
    			int sj = cwq, xj = a[i], tmp;
    			tmp = tr.Query(1, 1, cwq, xj, sj);
    			while (tmp)
    			{
    				bit.Update(n - tmp + 1, Abs(b[a[tmp]] - b[a[i]]));
    				sj = Find((b[a[tmp]] + b[a[i]]) / 2);
    				if (!sj) break;
    				if (sj >= a[tmp]) sj = a[tmp] - 1;
    				tmp = tr.Query(1, 1, cwq, xj, sj);
    			}
    			sj = a[i], xj = 1;
    			tmp = tr.Query(1, 1, cwq, xj, sj);
    			while (tmp)
    			{
    				bit.Update(n - tmp + 1, Abs(b[a[tmp]] - b[a[i]]));
    				xj = Find((b[a[tmp]] + b[a[i]]) / 2);
    				if (!xj) break;
    				if (xj <= a[tmp]) xj = a[tmp] + 1;
    				tmp = tr.Query(1, 1, cwq, xj, sj);
    			}
    			while (ask[pos].r == i)
    			{
    				ask[pos].ans = bit.Query(n - ask[pos].l + 1);
    				pos++;
    			}
    		}
    		tr.Update(1, 1, cwq, a[i], i);
    	}
    	sort(ask + 1, ask + q + 1, Cmp1);
    	for (int i = 1; i <= q; i++) Print(ask[i].ans), putchar('
    ');
    	return 0;
    }
    
  • 相关阅读:
    poj 2778 AC自己主动机 + 矩阵高速幂
    Web Services 指南之:Web Services 综述
    SQL多表连接查询(具体实例)
    HibernateUtil
    哈夫曼编码问题再续(下篇)——优先队列求解
    MySQL Merge存储引擎
    程序的入口及AppDelegate窗体显示原理
    几个免费的DNS地址
    kettle与各数据库建立链接的链接字符串
    【转】利用optimize、存储过程和系统表对mysql数据库表进行批量碎片清理释放表空间
  • 原文地址:https://www.cnblogs.com/Tian-Xing-Sakura/p/13823110.html
Copyright © 2020-2023  润新知