【JZOJ3719】K-D-Sequence

description

我们称一个数列为一个好的k-d 数列,当且仅当我们在其中加上最多k 个数之后,数列排序后为一个公差为d 的等差数列。

你手上有一个由n 个整数组成的数列a。你的任务是找到它的最长连续子串,使得满足子串为好的k-d 数列。


analysis

  • 满足条件的\([l,r]\)子序列中所有的数模\(d\)余数相同、没有相同的数,且最大值\(-\)最小值\(≤r-l+k+1\)

  • \(i\)循环扫一遍,然后维护一个单调递增的右端点\(j\),由性质可知在\(i\)左移的过程中右端点不会回调

  • 拿一个东西维护\([i,j]\)中间的元素的最大最小值、支持插入删除,比较好想的是哈希加线段树,查询就线段树上二分

  • 或者写个\(set\)维护元素,时间复杂度\(O(n\log n)\)


code

#pragma GCC optimize("O3")
#pragma G++ optimize("O3")
#include<stdio.h>
#include<string.h>
#include<algorithm>
#include<set>
#define MAXN 200005
#define INF 100000000000
#define ll long long
#define reg register ll
#define fo(i,a,b) for (i=a;i<=b;++i)
#define fd(i,a,b) for (i=a;i>=b;--i)

using namespace std;

ll n,k,d,i,j,ansl,ansr;
ll a[MAXN];
set<ll>s;

int main()
{
	freopen("sequence.in","r",stdin);
	freopen("sequence.out","w",stdout);
	scanf("%lld%lld%lld",&n,&k,&d);
	//fo(i,1,n)a[i]=read()+INF;
	fo(i,1,n)scanf("%lld",&a[i]),a[i]+=INF;
	if (!d)
	{
		i=1;
		while (i<=n)
		{
			j=i;
			while (a[i]==a[j+1] && j<n)++j;
			if (j-i>ansr-ansl)ansl=i,ansr=j;
			i=j+1;
		}
		printf("%lld %lld\n",ansl,ansr);
		return 0;
	}
	fo(i,1,n)
	{
		if (n-i<ansr-ansl)break;
		
		if (i<=j && j-i>=ansr-ansl)
		{
			set<ll>::iterator i1=s.begin(),i2=s.end();i2--;
			if ((*i2)-(*i1)<=j-i+1+k)ansl=i,ansr=j;
		}

		fo(j,j+1,n)
		{
			if (a[i]%d!=a[j]%d || s.count(a[j]/d))break;
			s.insert(a[j]/d);
			if (j-i>ansr-ansl)
			{
				set<ll>::iterator i1=s.begin(),i2=s.end();i2--;
				if ((*i2)-(*i1)<=j-i+1+k)ansl=i,ansr=j;
			}
		}
		s.erase(a[i]/d),--j;
	}
	printf("%lld %lld\n",ansl,ansr);
	return 0;
}
posted @ 2019-12-30 17:06  路人黑的纸巾  阅读(201)  评论(0编辑  收藏  举报