码迷,mamicode.com
首页 > 其他好文 > 详细

CF360B - Levko and Array 题解

时间:2020-10-19 23:07:28      阅读:32      评论:0      收藏:0      [点我收藏+]

标签:复杂   不难   不等式   stdin   clear   als   pre   eof   去掉   

本题解是 CF360B\(n\leq 2\times10^5\) 加强版。

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


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

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

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

考虑转移。显然有一个平方的转移,就是对于每个 \(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_i\geq a_j,a_i-ix\leq a_j-jx\)\(a_i<a_j,a_i+ix\geq a_j+jx\)。这样的话,考虑将「或」字两边的分别考虑,分别二维数点,这样总复杂度线性三次对数,会爆炸。我们需要一个线性二次对数或以下的算法。

注意到,如果最 primitive 的合法条件的 \(\leq\) 改成 \(\geq\),那么就很容易想到线性二次对数的算法:因为 \(a_i\geq 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<<"\n";
	return 0;
}

CF360B - Levko and Array 题解

标签:复杂   不难   不等式   stdin   clear   als   pre   eof   去掉   

原文地址:https://www.cnblogs.com/ycx-akioi/p/cf360b.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!