Codeforces Gym 100503F
题目链接:http://acm.hust.edu.cn/vjudge/problem/viewProblem.action?id=411106
题目大意:
给出一个长为n的非负数列,求删除最少的数,使任意相邻两个数差的绝对值不为1
1<=n<=10^5 ,1<=ai<=10^6
分析:
先吐槽一句:CF的Gym题居然要加文件……我一个上午就浪费在两行代码上……
首先这是一个经典问题的翻版:给出一个长为n的非负数列,求删除最少的数,使任意相邻两个数差的绝对值小于给定的d
如果用F[i]表示以i结尾的最长链,则有F[i]=max{F[j] | |a[i]-a[j]|<=d }+1
但是复杂度是O(n^2)的,难以承受。
于是我们琢磨一下这个式子:每次需要在范围[a[i]-d,a[i]+d]里找一个最大F值。
对呀这就是区间查询呀!我们建一棵以a为关键字,存储i的线段树,每次计算F[i]后用它去更新线段树(即a的值为a[i]的最大F[i]对应的i)
复杂度为O(nlogn)
在看原题,我们只要把DP方程改下,查询区间改成[0,a[i]-2]+[a[i],a[i]]+[a[i]+2,maxa]即可。因为a最大才10^6,复杂度可以接受。
核心思想:枚举->区间查询
好像有点抽象……具体看代码吧。
代码:
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <algorithm>
#define rep(i,x,y) for (int i=x;i<=y;i++)
#define dep(i,y,x) for (int i=y;i>=x;i--)
using namespace std;
const int maxn=100000+23;
int n,M,k,h,size,t[maxn*40],l[maxn],p[maxn],maxa,a[maxn],F[maxn],G[maxn],ans;
void init()
{
for (M=1;M<=maxa;M*=2);
memset(t,-1,sizeof(t));
}
int max2(int x,int y) //比较两个值对应DP值大小
{
if (x==-1) return y;
if (y==-1) return x;
if (F[x]>F[y]) return x; else return y;
}
void update(int x,int k)
{
x+=M; t[x]=k; x>>=1;
while (x>0)
{
t[x]=max2(t[x<<1],t[(x<<1)+1]);
x>>=1;
}
}
int query(int l,int r)
{
int ans=-1; l+=M; r+=M;
while (l<=r)
{
if (l==r) {ans=max2(ans,t[l]);break;}
if (l&1) ans=max2(t[l++],ans);
if (!(r&1)) ans=max2(t[r--],ans);
l>>=1;r>>=1;
}
return ans;
}
int get_max(int l,int r,int k)
{
if (l>r) return k;
int h=query(l,r);
return max2(h,k);
}
int main()
{
freopen("input.txt","r",stdin);
freopen("output.txt","w",stdout);
scanf("%d",&n);
maxa=0;
rep(i,1,n) {scanf("%d",&a[i]);maxa=max(maxa,a[i]);} //计算a最大值,即线段树范围
init(); //初始化
rep(i,1,n)
{
p[i]=get_max(a[i],a[i],-1); //区间【a[i],a[i]】
p[i]=get_max(0,a[i]-2,p[i]); //区间【0,a[i]-2】
p[i]=get_max(a[i]+2,maxa,p[i]); //区间【a[i]+2,maxa】
if (p[i]==-1) F[i]=1; else F[i]=F[p[i]]+1; //更新F[i]
update(a[i],i); //用F[i]更新线段树
}
ans=0;size=1;
rep(i,1,n) if (F[i]>ans) {ans=F[i];k=i;}
printf("%d\n",n-ans);
l[1]=a[k];
while (p[k]>=0)
{
k=p[k];
l[++size]=a[k];
}
dep(i,size,2) printf("%d ",l[i]);
printf("%d\n",l[1]);
return 0;
}
zkw线段树就是好啊……77ms,快到飞起……

浙公网安备 33010602011771号