CF360B - Levko and Array 题解

Posted ycx-akioi

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了CF360B - Levko and Array 题解相关的知识,希望对你有一定的参考价值。

本题解是 CF360B(nleq 2 imes10^5) 加强版。

一开始看到这题难度 2000,那完蛋了啊,我连 2000 的题都不会做了/ll。然后发现数据范围才 (2000)/xk。


答案 (x) 显然有单调性,先二分起来。

考虑固定那些没有被改变的柱子。那么不难发现,一个没有被改变的柱子序列合法,当且仅当任意一个相邻对 (i,j) 都满足 (|a_i-a_j|leq (j-i)x)。然后考虑一个 DP,求出最少需要改变多少个柱子,即最多保留多少个柱子。

我们考虑 (dp_i) 为考虑到 (i)(i) 保留,的最少的改变的柱子数。目标显然是 (left[maxlimits_{i=1}^n{dp_i+n-i}leq m ight])

考虑转移。显然有一个平方的转移,就是对于每个 (i) 暴力枚举它前面的决策 (j) 然后判一下比一下。这样总复杂度平方对数,原来的弱题就做完了。就这也配 2000?我 tm 直接秒切(((((

考虑优化。对于决策 (j),合法条件是 (|a_i-a_j|leq (i-j)x),更新式是 (dp_j+i-j-1)。那显然可以把更新式拆成 ((dp_j-j)+(i-1)),于是只要找到 (dp_j-j) 最小的合法决策即可。然后合法条件里有个绝对值,比较讨厌,考虑将它拆成 (a_igeq a_j,a_i-ixleq a_j-jx)(a_i<a_j,a_i+ixgeq a_j+jx)。这样的话,考虑将「或」字两边的分别考虑,分别二维数点,这样总复杂度线性三次对数,会爆炸。我们需要一个线性二次对数或以下的算法。

注意到,如果最 primitive 的合法条件的 (leq) 改成 (geq),那么就很容易想到线性二次对数的算法:因为 (a_igeq a_j) 所对应的不等式和 (a_i<a_j) 所对应的不等式,它要满足就显然只会满足真实的 (a_i,a_j) 大小情况对应的那个。那就很好办,直接忽略 (a_i,a_j) 的大小关系,把两个不等式对的并一下即可。但是该题是 (leq),如果并的话,那就每个决策都一定满足假的那个大小关系所对的不等式,那就每个决策都是合法的了……而如果把不符合的,也就是 (geq) 的给不算的话,那又无法撤销。事实上 (leq) 就是不能像 (geq) 那样顺利的。

我们考虑将「或」字改成「且」字,那就是可以像 (geq) 那样将 (a_i,a_j) 大小关系去掉的。那就可以表示成 ([a_j-jx,a_j+jx]subseteq[a_i-ix,a_i+ix])(这直接做还是需要二维数点的)。不过注意到这里的一个特殊的性质:对于 (i<j)(i) 的区间大小一定比 (j) 的区间大小小。这也就说明,我们原来是找那些 (j<i) 的满足这个条件的 (j),现在可以直接无视 (j<i) 了,因为 (j>i) 的话,(j) 的区间就更大,就不可能包含于 (i) 的区间了。于是可以调整一下 DP 顺序,将左端点排序,这样依然可以保证无后效性。这样一来,左端点这一维就不需要考虑了,于是变成一维数点了。就随便离散化 BIT 即可,小常数线性二次对数。

#include<bits/stdc++.h>
using namespace std;
#define int long long
#define pb push_back
const int inf=0x3f3f3f3f3f3f3f3f;
int lowbit(int x){return x&-x;}
void read(int &x){
	x=0;char c=getchar();bool ne=false;
	while(!isdigit(c))ne|=c==‘-‘,c=getchar();
	while(isdigit(c))x=(x<<1)+(x<<3)+(c^48),c=getchar();
	if(ne)x=-x;
}
const int N=200000;
int n,m;
int a[N+1];
int dp[N+1];
struct range{
	int l,r,id;
	range(int _l=0,int _r=0,int _id=0){l=_l,r=_r,id=_id;}
}rg[N+1];
bool cmp(range x,range y){
	if(x.l!=y.l)return x.l>y.l;
	return x.r<y.r;
}
vector<int> nums;
void discrete(){
	sort(nums.begin(),nums.end());
	nums.resize(unique(nums.begin(),nums.end())-nums.begin());
}
struct bitree{
	int mn[N+1];
	void init(){memset(mn,0x3f,sizeof(mn));}
	void chkmn(int x,int v){
		while(x<=n)mn[x]=min(mn[x],v),x+=lowbit(x);
	}
	int Mn(int x){
		int res=inf;
		while(x)res=min(res,mn[x]),x-=lowbit(x);
		return res;
	}
}bit;
bool chk(int x){
	nums.clear();
	for(int i=1;i<=n;i++)rg[i]=range(a[i]-i*x,a[i]+i*x,i),nums.pb(a[i]+i*x);
	discrete();
	sort(rg+1,rg+n+1,cmp);
	bit.init();
	for(int i=1;i<=n;i++){
		int fd=lower_bound(nums.begin(),nums.end(),rg[i].r)-nums.begin()+1;
		dp[rg[i].id]=min(rg[i].id-1,bit.Mn(fd)+rg[i].id-1);
		if(dp[rg[i].id]+n-rg[i].id<=m)return true;
		bit.chkmn(fd,dp[rg[i].id]-rg[i].id);
	}
	return false;
}
signed main(){
//	freopen("a.in","r",stdin);freopen("a.out","w",stdout); 
	read(n);read(m);
	for(int i=1;i<=n;i++)read(a[i]);
	int ans=1e10;
	for(int i=34;~i;i--)if(ans-(1ll<<i)>=0&&chk(ans-(1ll<<i)))ans-=1ll<<i;
	cout<<ans<<"
";
	return 0;
}

以上是关于CF360B - Levko and Array 题解的主要内容,如果未能解决你的问题,请参考以下文章

CodeForces - 361E Levko and Strings

Codeforces 360D Levko and Sets (数论好题)

cf C. Eugene and an array

洛谷 CF442C Artem and Array 紫 题解

C. Ayoub and Lost Array cf dp

codeforces CF718C Sasha and Array 线段树维护矩阵