[ARC150F] Constant Sum Subsequence
很有意思的题。
设 表示位置 后面的最小的满足 的 ,则问题可以抽象为:
直接做是 的。这个东西也不像是用线段树之类的东西可以优化的,打表发现也没有决策单调性,考虑从问题本身入手。
关键性质: 严格单调递增。证明很简单,可以在 对应的子序列后面加一个 ,所以 。
考虑用类似 CDQ 分治的思路,分治计算 。假设当前计算 区间 ,先计算左半边 ,然后考虑左对右的贡献,然后计算右半边 。
因为 是单调递增的,所以 。考虑对于每一个 ,左半边的 应当是这样的:
其中上下位置代表 ,小方格的左右位置代表 ,向右的线表示 。注意到左上角的两个 一定没有贡献,有贡献的一定是最右边的一段区间,这时候可以 seg beats 直接做了,但是其实还是没有必要。因为 严格单增,所以我们只需要考虑第三个小方格的贡献即满足 的最小的 ,因为用 去更新 之后不可能用 去更新 ,因为两者的 是一样的。所以只需要枚举每一个 ,找到满足要求的最小的 去更新右半部分的数组即可,需要注意边界问题。复杂度 ,代码并不难写。
注意观察性质可以使问题得到很大的简化。其中分治的本质是找到一个合适的分界线来寻找最优决策点。
int n,m,a[1500010],lst[200010],pr[1500010],f[200010];
vector<int> ve[200010];
inline int nex(int i,int j)
{
auto it=upper_bound(ve[j].begin(),ve[j].end(),(i-1)%n+1);
if(it==ve[j].end())return ve[j].front()+((i-1)/n+1)*n;
return *it+(i-1)/n*n;
}
int find(int L,int R,int x)
{
int mid;
while(L<R)
{
mid=L+((R-L)>>1);
if(f[mid]>=x)R=mid;
else L=mid+1;
}
return L;
}
void solve(int l,int r)
{
if(l==r)return;
int mid=l+((r-l)>>1),pos,pre;
solve(l,mid);
for(int i=1;i<=r-l;++i)
{
pos=nex(f[mid],i),pre=(pos-1)/n*n+pr[(pos-1)%n+1];
pre=find(l,mid,pre);
if(pre+i>r)continue;
pre=max(pre,mid+1-i);
assert(pre+i>mid);
assert(pre+i<=r);
assert(nex(f[pre],i)==pos);
assert(pre==l||nex(f[pre-1],i)!=pos);
f[pre+i]=max(f[pre+i],pos);
}
solve(mid+1,r);
}
inline void mian()
{
read(n,m);
for(int i=1;i<=n;++i)read(a[i]),ve[a[i]].eb(i);
for(int i=1;i<=n;++i)lst[a[i]]=i-n;
for(int i=1;i<=n;++i)pr[i]=lst[a[i]],lst[a[i]]=i;
solve(0,m),write(f[m]);
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通