【学习笔记】[ARC150F] Constant Sum Subsequence

第一眼看上去,这道题一点都不套路

第二眼看上去,大概是要考 d p dp dp优化,那没事了,除非前面 3 3 3道题都做完了否则直接做这道题肯定很亏

首先我们要定义一个好的状态。废话

f s f_{s} fs表示 B B B序列的和为 s s s时,能达到的 A A A序列的最大长度,也就是最紧的限制。

这一步定义非常自然,到这里都没有任何问题

不过直接暴力转移复杂度 O ( S 2 log ⁡ n ) O(S^2\log n) O(S2logn),考场上好像只能得到 20 p t s 20pts 20pts

似乎有根号乱搞的做法,但是这道题 n n n, S S S比较大所以会被卡掉

这是逼着我们想正解啊

不管了,根号乱搞比较好想,而且确实也是考场上性价比最高的做法

cdq \text{cdq} cdq分治的想法挺阳间的,应该可以学一下

乱胡一下吧,不过考场上可能我也不会写 考虑计算区间 [ l , r ] [l,r] [l,r] d p dp dp值,显然我们知道转移的这个数不会超过 [ l , r ] [l,r] [l,r]这个区间的长度。

发挥 bot \text{bot} bot的能力 我们有转移式 f s = max ⁡ x ≤ s nxt ( f s − x , x ) f_s=\max_{x\le s}\text{nxt}(f_{s-x},x) fs=maxxsnxt(fsx,x),并且 f s f_s fs是单增的,因此 ∀ i ∈ [ m i d + 1 , r ] , f i > f m i d \forall i\in [mid+1,r],f_i>f_{mid} i[mid+1,r],fi>fmid。先枚举一个 x x x,则我们只需要考虑可能对右区间有贡献的 s s s,即满足 nxt ( f s , x ) = nxt ( f m i d , x ) \text{nxt}(f_{s},x)=\text{nxt}(f_{mid},x) nxt(fs,x)=nxt(fmid,x)。不难猜想,这些 s s s 形成了一个区间,并且这个区间的左端点就是最小的 i i i满足 f i ≥ front ( f m i d , x ) f_i\ge \text{front}(f_{mid},x) fifront(fmid,x),于是对于这个区间,贡献是相同的,而对应的右半部分下标是连续的,因此用一个线段树维护即可。

复杂度两个 log ⁡ \log log应该可以通过吧?

代码先咕了

似乎代码也不是特别长,出题人还是挺良心的

#include<bits/stdc++.h> #define pb push_back #define ll long long #define fi first #define int ll #define se second using namespace std; const int N=2e5+5; const int M=2e6+5; int n,S,a[M],f[N],t[N<<2]; vector<int>v[N]; int nxt(int x,int y){ int z=x%n;if(!z)z=n; int p=upper_bound(v[y].begin(),v[y].end(),z)-v[y].begin(); if(p==v[y].size())return x+v[y][0]-z+n; return x+v[y][p]-z; } int pre(int x,int y){ int z=x%n;if(!z)z=n; int p=upper_bound(v[y].begin(),v[y].end(),z)-v[y].begin()-1; if(~p)return x-z+v[y][p]; return x-z+v[y].back()-n; } void upd(int p,int l,int r,int ql,int qr,int x){ if(ql<=l&&r<=qr){ t[p]=max(t[p],x); return; } int mid=l+r>>1; if(ql<=mid)upd(p<<1,l,mid,ql,qr,x); if(mid<qr)upd(p<<1|1,mid+1,r,ql,qr,x); } int qry(int p,int l,int r,int x){ if(l==r)return t[p]; int mid=l+r>>1; if(x<=mid)return max(qry(p<<1,l,mid,x),t[p]); return max(qry(p<<1|1,mid+1,r,x),t[p]); } void solve(int l,int r){ if(l==r){ f[l]=max(f[l],qry(1,1,S,l)); return; } int mid=l+r>>1; solve(l,mid); for(int i=1;i<=r-l+1;i++){ int L=lower_bound(f+l,f+mid+1,pre(f[mid],i))-f; assert(L<=mid); L=max(mid+1,L+i);int R=min(mid+i,r); if(L<=R){ upd(1,1,S,L,R,nxt(f[mid],i)); } } solve(mid+1,r); } signed main(){ ios::sync_with_stdio(false); cin.tie(0),cout.tie(0); cin>>n>>S; for(int i=1;i<=n;i++)cin>>a[i],v[a[i]].pb(i); for(int i=1;i<=S;i++)f[i]=nxt(0,i); solve(1,S); cout<<f[S]; }

__EOF__

本文作者仰望星空的蚂蚁
本文链接https://www.cnblogs.com/cqbzly/p/17530023.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   仰望星空的蚂蚁  阅读(19)  评论(0编辑  收藏  举报  
相关博文:
阅读排行:
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 记一次.NET内存居高不下排查解决与启示
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· DeepSeek 开源周回顾「GitHub 热点速览」
历史上的今天:
2022-02-25 【题解】最小公倍数计数
2022-02-25 【题解】约数之和。
2022-02-25 【题解】Lucas的数论
点击右上角即可分享
微信分享提示