1 条题解
-
0
提供另一种维护方式:使用分块,时间复杂度 ,但是常数小。
题意
给定一个序列,每次询问单点修改,再求把区间 划分为若干段使每段的和构成的序列是交替的。
性质分析
这部分另外的题解已经讲得很详细了,但为了形成一篇完整的题解,就在这略提几句。
首先发现在最优方案下,一定是划分成一些大段和一些长度为 1 的小段,因为如果小段长度 ,把剩下的分到大段一定不劣。
在最优方案下,合法大段的充要条件是 且 。注意到在不保证最优条件下,这样划分也可以得到正确段数。考虑如下证明:假设一个长度 的小段 ,左侧有大段 ,右侧大段 ,若 ,那么把右侧变为 依然合法,反之同理拓展左边;若 在最左侧,若 ,那么把 移动到 ,否则新增一段 ,在最右边同理对称一下。
成满足上述条件的段为好段,发现找好段划分满足最优条件下充要条件,一定可以找出不劣于最优解的答案,且已经证明这样求出的答案不会出现使答案偏大的不合法情况,于是只要计算好段划分即可。
现在的问题变为了,有一些区间 ,要选出一些区间使它们无交,最多选出多少个区间。可以贪心,按照右端点从小到大,尽量选择右端点小的。设 表示若上一个好段结尾为 ,那下一个可以接在它后面的好段的最小右端点。于是我们会 了,暴力修改 ,然后询问时候暴力计算跳了多少次 。需要稍微讨论一下开头结尾是否存在小段,这也是简单的,因为若不存在小段,根据贪心思想第一个大段也被确定了。
感觉每次只修改一位,把整个 重算一遍过于浪费。发现有重要性质: 为 级别。考虑从下个好段中任意一点向左右拓展,每拓展一次需满足当前和 拓展的位置,也就是说和至少翻倍,因此 次拓展后和大于任意一个相邻的数。有这个性质之后,每次修改只会影响 个位置的 。
好像不是略提几句了
快速跳
可以 LCT,但那太蠢了,
而且我不会。也可以线段树,是 的,但是常数不小而且不是很好写。注意到这部分和 P3203 非常像。借鉴那题的分块,维护 和 表示从当前块内一点第一次跳到块外的位置和跳跃次数。
修改的时候,假设 发生变动的区间为 ,首先修改 内的 和 ,然后修改剩下的和 在一个块内的位置的 和 ,修改可以递推,单个位置 ,这部分最多修改 和点。
询问时候先跳 ,再跳 ,两段跳跃次数都不超过 。
因此最后的复杂度是 ,稍劣于线段树,但是常数小啊,一发交上去是目前的最优解。
Code
代码不长,但是有一些细节。
#include<bits/stdc++.h> using namespace std; #define inf 0x3f3f3f3f #define mod 998244353 #define N 250010 #define int long long #define szi sizeof(int) #define il inline int n,q,sq,a[N],x,y,l,ll,r,rr,nxt[N],p[N],t[N],c[N],ans; il void cmax(int &x,int y){x=(x>y)?x:y;return ;} il void cmin(int &x,int y){x=(x<y)?x:y;return ;} il void upd(int l,int r){ for(int i=r;i>=l;--i){ nxt[i]=nxt[i+1]; for(int j=i+2,s=a[i+2];j<=n&&j-i<=65;s+=a[++j]) if(s>a[i+1]&&s>a[j+1]){cmin(nxt[i],j);break;} if(p[nxt[i]]==p[i]) t[i]=t[nxt[i]],c[i]=c[nxt[i]]+2; else t[i]=nxt[i],c[i]=2; } for(int i=l-1;i>=0&&p[i]==p[l];--i){ if(p[nxt[i]]==p[i]) t[i]=t[nxt[i]],c[i]=c[nxt[i]]+2; else t[i]=nxt[i],c[i]=2; } return ; } il int calc(int l,int r){ int res=0; --l; if(l>=r) return -inf; while(t[l]<r) res+=c[l],l=t[l]; while(nxt[l]<r) res+=2,l=nxt[l]; return res; } signed main(){ // freopen("a.in","r",stdin); // freopen("a.out","w",stdout); scanf("%lld",&n),sq=sqrt(n); for(int i=1;i<=n;++i) scanf("%lld",&a[i]); for(int i=0;i<=n;++i) p[i]=i/sq+1; p[n+1]=p[n]+1; nxt[n+1]=n+1,upd(0,n),scanf("%lld",&q); while(q--){ scanf("%lld%lld%lld%lld",&x,&y,&l,&r),++l,a[x]=y; upd(max(0ll,x-65),x-1),ans=1,ll=r+1,rr=l-1; for(int i=l,s=a[l];i<r;s+=a[++i]) if(s>a[i+1]){ll=i+1;break;} for(int i=r,s=a[r];i>l;s+=a[--i]) if(s>a[i-1]){rr=i-1;break;} cmax(ans,calc(l,r)+1),cmax(ans,calc(ll,r)+2); cmax(ans,calc(l,rr)+2),cmax(ans,calc(ll,rr)+3); printf("%lld\n",ans); } return 0; }
- 1
信息
- ID
- 7483
- 时间
- 3000ms
- 内存
- 1024MiB
- 难度
- 10
- 标签
- 递交数
- 1
- 已通过
- 1
- 上传者