1. 程式人生 > >【BZOJ4167】永遠的竹筍采摘 分塊+樹狀數組

【BZOJ4167】永遠的竹筍采摘 分塊+樹狀數組

() for 同時 mat ++ cls sqrt clas freopen

【BZOJ4167】永遠的竹筍采摘

技術分享

技術分享

題解:我們考慮有多少點對(a,b)滿足a與b的差值是[a,b]中最小的。以為是隨機數據,這樣的點對數目可能很少,實測是O(n)級別的,那麽我們已知了有這麽多可能對答案造成貢獻的點對,如何將它們求出來呢?

考慮分塊,因為所有數大小在[1,n]中,我們可以對於每個塊,預處理出整個塊到所有數的最小差值。然後從右往左枚舉每一個點,再枚舉右面所有的塊,如果這個塊到當前數的差值比之前的要小,那就暴力進入塊中掃一遍。與此同時,我們需要知道是否已經存在這樣的點對,被當前的點對完全包含且差值更小。這個可以用樹狀數組搞定。

最後,我們得到所有的點對,問題就變成了在數軸上選取k個互部相交的線段,使得線段權值和最小。跑個DP就行了。

#include <cstdio>
#include <cstring>
#include <iostream>
#include <cmath>
#include <algorithm>
using namespace std;
const int maxn=60010;
int n,m,B,cnt,minn;
int s[maxn],v[maxn],p[maxn];
int cls[250][maxn],f[2][maxn],to[500000],next[500000],head[maxn],val[500000];
int rd()
{
	int ret=0,f=1;	char gc=getchar();
	while(gc<‘0‘||gc>‘9‘)	{if(gc==‘-‘)f=-f;	gc=getchar();}
	while(gc>=‘0‘&&gc<=‘9‘)	ret=ret*10+gc-‘0‘,gc=getchar();
	return ret*f;
}
int z(int x)
{
	return x>0?x:-x;
}
void updata(int x,int val)
{
	for(int i=x;i<=n;i+=i&-i)	s[i]=min(s[i],val);
}
int query(int x)
{
	int i,ret=1<<30;
	for(i=x;i;i-=i&-i)	ret=min(ret,s[i]);
	return ret;
}
void test(int a,int b)
{
	int c=z(v[b]-v[a]);
	a++,b++,minn=min(minn,c);
	if(query(b)<=c)	return ;
	updata(b,c);
	to[cnt]=a,val[cnt]=c,next[cnt]=head[b],head[b]=cnt++;
}
int main()
{
	//freopen("bz4168.in","r",stdin);
	n=rd(),m=rd(),B=ceil(sqrt(n));
	int i,j,k,last;
	for(i=0;i<n;i++)	v[i]=rd();
	memset(cls,0x3f,sizeof(cls));
	memset(s,0x3f,sizeof(s));
	for(i=0;i<n;i+=B)
	{
		for(j=i;j<i+B&&j<n;j++)	p[v[j]]=1;
		for(last=-1<<30,j=1;j<=n;j++)	cls[i/B][j]=min(cls[i/B][j],j-last),last=p[j]?j:last;
		for(last=1<<30,j=n;j>=1;j--)	cls[i/B][j]=min(cls[i/B][j],last-j),last=p[j]?j:last;
		for(j=i;j<i+B&&j<n;j++)	p[v[j]]=0;
	}
	memset(head,-1,sizeof(head));
	for(i=n-1;i>=0;i--)
	{
		minn=1<<30;
		for(j=i+1;j<i/B*B+B&&j<n;j++)	if(v[j]!=v[i]&&z(v[j]-v[i])<minn)	test(i,j);
		for(j=i/B+1;j*B<n;j++)	if(cls[j][v[i]]<minn)	for(k=j*B;k<j*B+B&&k<n;k++)	if(v[k]!=v[i]&&z(v[k]-v[i])<minn)	test(i,k);
	}
	for(k=1;k<=m;k++)
	{
		for(i=0;i<=n;i++)	f[k&1][i]=1<<30;
		for(i=1;i<=n;i++)
		{
			f[k&1][i]=f[k&1][i-1];
			for(j=head[i];j!=-1;j=next[j])	f[k&1][i]=min(f[(k&1)^1][to[j]-1]+val[j],f[k&1][i]);
		}
	}
	printf("%d\n",f[m&1][n]);
	return 0;
}

【BZOJ4167】永遠的竹筍采摘 分塊+樹狀數組