ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

CF1508F. Optimal Encoding

2021-04-23 22:04:57  阅读:202  来源: 互联网

标签:CF1508F le suc Encoding int sqrt 端点 区间 Optimal


给出个排列\(a\),并且给出\(m\)个区间\([L_i,R_i]\)。

定义一个排列\(b\)对于某区间\([L,R]\)合法,当且仅当对于所有\(i,j\in [L,R],a_i<a_j\),有\(b_i<b_j\)。对于某个区间集合合法,当且仅当对于每个区间都合法。

前\(k\)个区间的答案为:构造一个有向图\(G\),使得如果排列\(b\)对于这\(k\)个区间形成的集合合法,当且仅当对于所有\((u,v)\in E_G\),有\(b_u<b_v\)。问最小的\(|E_G|\)。

对\(k\in [1,m]\)分别求答案。

\(n\le 2.5*10^4,q\le 10^5\)


普通的想法:先对每个区间中,如果有\(a_u<a_v\)就连边\((u,v)\)。构出图之后,删掉一些边,使得仍然满足大小关系。如果\((u,v)\)保留,当且仅当不存在\(x\neq u,v\),存在路径\(u\to x\to v\)。

结合区间的性质思考问题:假设\(u<v\)。如果\((u,v)\)存在,那么存在同时包含\(u,v\)的区间。在这个条件下,如果\((u,v)\)被删去:如果\([u,v]\)内存在点\(t\)满足\(a_u<a_t<a_v\),显然要删去;否则,存在一条路径\(u\to p_1\to p_2\to \dots p_k\to v\),必定有\(p_k<u\),所以\(p_k,u,v\)在一个区间内,于是就只需要考虑\(k=1\)的情况。也就是说,找到区间\(i\)满足\([u,v]\subseteq [L_i,R_i]\),且\(L_i\)最小,在\([L_i,v]\)中找到\(t\)满足\(a_t>a_u\),最小化\(a_t\),然后判断是否有\(a_u<a_t<a_v\)。

进一步地可以推断出:从某个点\(u\)发出的有向边至多两条,一条向左一条向右。以向右的为例,找到包含\(u\)的区间最大右端点\(maxr(u)\),在\([u,maxr_u]\)中找到\(t\)满足\(a_t>a_u\),\(a_t\)最小(记作\(suc_r(u)\))。向左类似地定义\(suc_l(u)\)。\((u,suc_l(u)),(u,suc_r(u))\)都是边的候选。

一种快速的做法:类似地,换成\(a_t<a_u\)且\(a_t\)最大定义\(pre_r(u),pre_l(u)\)。有结论:假设\(u<v\),\((u,v)\)被保留当且仅当\(suc_r(u)=v \and pre_l(v)=u\)。证明:考虑上面描述的\((u,v)\)被删去的情况,可以发现在那情况中,\(t\)可以替换\(pre_l(v)\),所以\(pre_l(v)\neq u\)。于是就可以搞出\(O(n^2)\)的简单做法。由于\(n\)小并且开7s,这种方法可以随便过。

题解做法:

以右边为例,对于每个\(u\),找到所有可能成为\(suc_r(u)\)的点,即每个\(v\)是区间\([u,v]\)满足\(a_v>a_u\)的\(a_v\)最小的\(v\)。显然\(v_i<v_{i+1},a_v>a_{i+1}\)。

显然,随着时间增加,\(suc_r(u)\)右移。

考虑某个\((u,v_i)\)的存在时间,由以下三个参数决定:

  1. 第一次出现区间同时包含\(u,v_i\)的时间\(t_1\)。
  2. \((u,v_i)\)第一次被隐藏(即此时找到了点\(t<u\)满足\(t,u,v_i\)在同一个区间,且\(a_u<a_t<a_{v_i}\))的时间\(t_2\)。
  3. 第一次出现区间同时包含\((u,v_{i+1})\)的时间\(t_3\)。

其存在时间为\([t_1,\min(t_2,t_3))\)。

\(u,v_i\)第一次被同时包含的时间可以搞个二维偏序求出。至于\(t_2\),分别求出左右的\(v_i\)序列,用类似归并的方式搞。对于右边的某个\(v_i\)来说,可能的\(t\)是左边序列(位置递减)中一段后缀,只需要取后缀的第一个作为\(t\)。于是\(v_i\)对应的\(t\)是一定的,接下来要找到最小的\(j\)满足\(L_j\le t,v_i\le R_j\),也可以二维偏序解决之。

有用的\((u,v)\)只有\(O(n\sqrt q)\)个。证明考虑对于某个区间\([l,r]\),如果\(u,v\)有连边,则\(a_u,a_v\)在区间内的权值离散化后连续。把它变成\([l\pm 1,r \pm 1]\),发现总量的变化量是\(O(1)\)的。假设跑个莫队,那么总数不会超过\(O(n\sqrt q)\)。

如何找到有用的\((u,v)\):直接跑莫队,用set维护相对顺序,可以做到\(O(n\sqrt q\lg n)\);也可以回滚莫队,把当前块以及之后的点排序,用链表维护,然后倒着删当前块后面的点(得知每个点加入时的前驱后继)。一开始左端点固定在块头(平常是块尾),右端点右移,在链表中加点,对于每个询问移动左端点过来(每次删掉左端点),询问后再把左端点移动回去。那么这样做除了排序之外是\(O(n\sqrt q)\)。

于是总时间\(O(n\sqrt q\lg n)\)。


using namespace std;
#include <bits/stdc++.h>
#define N 25005
#define M 100005
int n,m;
int a[N];
int lmin[N],rmax[N];
int suc[N][2],pre[N][2];
int ans;
void work(int l,int r){
	for (int i=l;i<=r;++i){
		for (int &j=rmax[i];j<=r;++j)
			if (a[j]>a[i]){
				int &t=suc[i][0];
				if (a[j]<a[t])
					ans+=(pre[j][1]==i)-(pre[t][1]==i),t=j;
			}
			else{
				int &t=pre[i][0];
				if (a[j]>a[t])
					ans+=(suc[j][1]==i)-(suc[t][1]==i),t=j;
			}
		for (int &j=lmin[i];j>=l;--j)
			if (a[j]>a[i]){
				int &t=suc[i][1];
				if (a[j]<a[t])
					ans+=(pre[j][0]==i)-(pre[t][0]==i),t=j;
			}
			else{
				int &t=pre[i][1];
				if (a[j]>a[t])
					ans+=(suc[j][0]==i)-(suc[t][0]==i),t=j;
			}
	}
}
int main(){
	scanf("%d%d",&n,&m);
	for (int i=1;i<=n;++i)
		scanf("%d",&a[i]);
	a[0]=n+1,a[n+1]=0;
	for (int i=1;i<=n;++i){
		lmin[i]=i-1,rmax[i]=i+1;
		suc[i][0]=suc[i][0]=0;
		pre[i][0]=pre[i][1]=n+1;
	}
	for (int i=1;i<=m;++i){
		int l,r;
		scanf("%d%d",&l,&r);
		work(l,r);
		printf("%d\n",ans);
	}
	return 0;
}

标签:CF1508F,le,suc,Encoding,int,sqrt,端点,区间,Optimal
来源: https://www.cnblogs.com/jz-597/p/14695626.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有