CF1416E-Split【dp,set】

正题

题目链接:https://www.luogu.com.cn/problem/CF1416E


题目大意

给出\(n\)个正整数的一个序列\(a_i\),你要把\(a_i\)拆成两个正整数的和\(b_{2i},b_{2i+1}\),要求使得\(b\)的相同连续段最少。

\(1\leq n\leq 5\times 10^5,1\leq a_i\leq 10^9\)


解题思路

考虑求最大的相邻相同数目,先考虑暴力的\(dp\),设\(f_{i,j}\)表示分解完\(a_i\)\(b_{2i+1}=j\)时的方案,那么有转移方程

\[f_{i,j}=\max\{f_{i-1,k}+[k=a_i-j]\}+[2j=a_i] \]

而且不难发现对于一个\(i\)来说它的所有\(f_{i,j}\)在加上\([2j=a_i]\)之前差距不会超过\(1\),而且我们显然只有可能从最大值转移。

对于\(2j=a_i\)的情况很难处理,我们可以先考虑都是奇数的情况。

首先开始都有\(f_{1,j}=0\),可以记为区间\([1,a_{1}-1]\),然后到第二个对于一个最大的\(j\),我们可以转移到\(a_2-j\)(如果合法)。同样的我们可以翻转之后得到一个新的最大区间\([l,r]\),当某次之后这个区间空了那么因为上面提到的\(f_{i,j}\)的差距不会超过\(1\),所以最大值不变然后区间变回\([1,a_{i}-1]\)

之后考虑\(a_i\)有偶数的情况怎么处理,此时会出现的问题就是:如果\(\frac{a_i}{2}\)加之前是最大值,那么加上之后就变为了唯一的最大值,这个很好处理,而如果之前不是最大值,那么加了之后就变为了最大值。

这个时候有可能会在区间之外出现一些单点的最大值,我们可以用\(set\)来储存这些位置,至于翻转之后所有的位置\(x\)都会变为\(a_i-x\),那么可以储存一个\(x\)表示实际上这个位置的值为\(x\times f+buf\)的情况,这样我们就可以快速的翻转然后把不合法的值去掉就好了。

时间复杂度:\(O(n\log n)\)


code

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<set>
#define ll long long
using namespace std;
const ll N=5e5+10;
ll T,n,ans,l,r,flag,f,buf,a[N];
set<ll> s;
void solve(ll lim){
	flag=1;
	if(l<=r){
		if(lim<=l)l=1,r=0;
		else l=lim-l,r=lim-min(r,lim-1),swap(l,r),flag=0;
	}
	f=f*-1;buf=lim-buf;
	while(!s.empty()){
		ll w=(*s.begin())*f+buf;
		if(w<1||w>=lim)s.erase(s.begin());
		else break;
	}
	while(!s.empty()){
		ll w=(*(--s.end()))*f+buf;
		if(w<1||w>=lim)s.erase(--s.end());
		else break;
	}
	return;
}
signed main()
{
	scanf("%lld",&T);
	while(T--){
		s.clear();f=ans=1;buf=flag=0;
		scanf("%lld",&n);
		for(ll i=1;i<=n;i++)
			scanf("%lld",&a[i]);
		if(a[1]&1)l=1,r=a[1]-1,ans++;
		else l=r=a[1]/2;
		for(ll i=2;i<=n;i++){
//			if(s.size())printf("%d\n",*s.begin());
 			if(a[i]&1){
				solve(a[i]);ans++; 
				if(s.empty()&&flag)l=1,r=a[i]-1,ans++;
			}
			else{
				if(s.find((a[i]/2-buf)*f)!=s.end()||a[i]/2>=l&&a[i]/2<=r)
					s.clear(),l=r=a[i]/2;
				else solve(a[i]),s.insert((a[i]/2-buf)*f),ans++;
			}
		}
		printf("%lld\n",ans);
	}
	return 0;
}
posted @ 2021-11-15 19:41  QuantAsk  阅读(58)  评论(0编辑  收藏  举报