正题
题目链接:https://www.luogu.com.cn/problem/CF1416E
题目大意
给出nnn个正整数的一个序列aia_iai,你要把aia_iai拆成两个正整数的和b2i,b2i+1b_{2i},b_{2i+1}b2i,b2i+1,要求使得bbb的相同连续段最少。
1≤n≤5×105,1≤ai≤1091\leq n\leq 5\times 10^5,1\leq a_i\leq 10^91≤n≤5×105,1≤ai≤109
解题思路
考虑求最大的相邻相同数目,先考虑暴力的dpdpdp,设fi,jf_{i,j}fi,j表示分解完aia_iai且b2i+1=jb_{2i+1}=jb2i+1=j时的方案,那么有转移方程
fi,j=max{fi−1,k+[k=ai−j]}+[2j=ai]f_{i,j}=\max\{f_{i-1,k}+[k=a_i-j]\}+[2j=a_i]fi,j=max{fi−1,k+[k=ai−j]}+[2j=ai]
而且不难发现对于一个iii来说它的所有fi,jf_{i,j}fi,j在加上[2j=ai][2j=a_i][2j=ai]之前差距不会超过111,而且我们显然只有可能从最大值转移。
对于2j=ai2j=a_i2j=ai的情况很难处理,我们可以先考虑都是奇数的情况。
首先开始都有f1,j=0f_{1,j}=0f1,j=0,可以记为区间[1,a1−1][1,a_{1}-1][1,a1−1],然后到第二个对于一个最大的jjj,我们可以转移到a2−ja_2-ja2−j(如果合法)。同样的我们可以翻转之后得到一个新的最大区间[l,r][l,r][l,r],当某次之后这个区间空了那么因为上面提到的fi,jf_{i,j}fi,j的差距不会超过111,所以最大值不变然后区间变回[1,ai−1][1,a_{i}-1][1,ai−1]。
之后考虑aia_iai有偶数的情况怎么处理,此时会出现的问题就是:如果ai2\frac{a_i}{2}2ai加之前是最大值,那么加上之后就变为了唯一的最大值,这个很好处理,而如果之前不是最大值,那么加了之后就变为了最大值。
这个时候有可能会在区间之外出现一些单点的最大值,我们可以用setsetset来储存这些位置,至于翻转之后所有的位置xxx都会变为ai−xa_i-xai−x,那么可以储存一个xxx表示实际上这个位置的值为x×f+bufx\times f+bufx×f+buf的情况,这样我们就可以快速的翻转然后把不合法的值去掉就好了。
时间复杂度:O(nlogn)O(n\log n)O(nlogn)
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;
}