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 (数论好题)