Rainbow Subarray

题目:

TEN is an exciting push-your-luck and auction board game. Players may push their luck to draw more cards and use currency to buy additional cards in their attempt to build the longest number sequence in each color. Here let's consider a related problem to the game.

Given a sequence a1,a2,⋯,an𝑎1,𝑎2,⋯,𝑎𝑛 of length n𝑛, we say its continuous subarray al,al+1,al+2,⋯,ar𝑎𝑙,𝑎𝑙+1,𝑎𝑙+2,⋯,𝑎𝑟 is a rainbow subarray if ai+1−ai=1𝑎𝑖+1−𝑎𝑖=1 for all l≤i<r𝑙≤𝑖<𝑟. Specifically, a subarray of length 11 is always a rainbow subarray.

You can perform at most k𝑘 operations. Each operation allows you to increase or decrease an element in the sequence by one. Calculate the maximum possible length of the longest rainbow subarray after performing the operations.

Input

There are multiple test cases. The first line of the input contains an integer T𝑇 indicating the number of test cases. For each test case:

The first line contains two integers n𝑛 and k𝑘 (1≤n≤5×1051≤𝑛≤5×105, 0≤k≤10150≤𝑘≤1015) indicating the length of the sequence and the maximum number of operations you can perform.

The second line contains n𝑛 integers a1,a2,⋯,an𝑎1,𝑎2,⋯,𝑎𝑛 (1≤ai≤1091≤𝑎𝑖≤109) indicating the sequence.

It's guaranteed that the sum of n𝑛 of all test cases will not exceed 5×1055×105.

Output

For each test case output one line containing one integer indicating the maximum possible length of the longest rainbow subarray after performing at most k𝑘 operations.

样例输入:

5
7 5
7 2 5 5 4 11 7
6 0
100 3 4 5 99 100
5 6
1 1 1 1 1
5 50
100 200 300 400 500
1 100
3

样例输出:

4
3
5
1
1

题目大意:

        给一个长度为n的数组,最多操作k次,给任意一个数加一或者减一算一次操作,

问最长的连续子数组长度满足:

取区间[l,r],a[i+1]-a[i]=1,l<=i<=r;

思路:

        发现a[i+1]-a[i]=1,可以同构成a[i+1]-(i+1)=a[i]-i,所以取b[i]=a[i]-i

于是题目转化成求最长相等连续子数组b[i]的长度。(虽然不转化也没啥影响)

        因为将n个数变成n个数的中位数操作最小,所以对于每个区间最小的操作数

就是将其都转化成中位数的操作数。

        现在来考虑操作数等于多少。我们将n个数分成两堆,一堆大于中位数,

一堆小于中位数,设前者的数量为l,和为suml,后者数量为r,和为sumr

中位数为mid,那么操作数就是suml-mid*l+mid*r-sumr

        容易知道每加入一个新数操作数只加不减,也就是说操作数具有单调性,

那么就可以用尺取法来O(n)的来取区间。至于如何动态的确定区间中位数,

可以用对顶堆来实现。

注意点:

erase的时候要用find(元素),不可以直接删除元素,当某一堆没有元素时要特判。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int mod=1e9+7;
const int N=5e5+10;

int a[N],b[N];
ll sums,sumb,ans;
ll l,r,n,k;
multiset<ll,greater<int> > big;
multiset<ll,less<int> > small;
int main(){
	ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
	int t;cin>>t;
	auto blance=[&]()->void{
		int ls=small.size(),lb=big.size();
		if(ls>lb+1){
			int num=*small.begin();
			big.insert(num);
			small.erase(small.find(num));
			sumb+=num;
			sums-=num;
		}
		else if(lb>ls+1){
			int num=*big.begin();
			small.insert(num);
			big.erase(big.find(num));
			sumb-=num;
			sums+=num;
		}
	};
	auto getmid=[&]()->ll{
		int ls=small.size(),lb=big.size();
		if((ls+lb)&1){
			if(ls>lb){
				return *small.begin();
			}
			else{
				return *big.begin();
			}
		}
		else{
			return (((*small.begin())+(*big.begin()))>>1);
		}
	};
	auto cal=[&]()->ll{
		ll mid=getmid();
		int ls=small.size(),lb=big.size();
		return sums-mid*ls+mid*lb-sumb;
	};
	auto check=[&]()->bool{
		return (cal()<=k)?1:0;
	};
	auto add=[&](ll x)->void{
		if(small.empty()){
			if(big.size()){
				int num=*big.begin();
				if(x<num){
					big.erase(num);
					small.insert(num);
					big.insert(x);
					sumb=x;
					sums=num;
				}
				else{
					small.insert(x);
					sums=x;
				}
			}
			else{
				small.insert(x);
				sums=x;
			}
			return;
		}
		ll pos=*small.begin();
		if(x>pos){
			small.insert(x);
			sums+=x;
		}
		else{
			big.insert(x);
			sumb+=x;
		}
		blance();
	};
	auto erase=[&](ll x)->void{
		if(small.count(x)){
			small.erase(small.find(x));
			sums-=x;
		}
		else{
			big.erase(big.find(x));
			sumb-=x;
		}
		blance();
	};
	while(t--){
		ans=sums=sumb=0;
		big.clear();
		small.clear();
		cin>>n>>k;
		for(int i=1;i<=n;i++){
			cin>>a[i];
			b[i]=a[i]-i;
		}
		l=1;
		for(r=1;r<=n;r++){
			add(b[r]);
			while(!check()){
				erase(b[l++]);
			}
			ans=max(ans,1ll*(r-l)+1);
		}
		cout<<ans<<endl;
	}
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值