AtCoder Beginner Contest 381

这场比赛打的冷汗直流,然后无奈寄掉。

C - 11/22 Substring

本以为直接暴力就可以,但是需要加前缀和优化,一个正向处理,一个反向处理,然后查找/

abc381_d

赛前2分钟hack掉自己的代码,然后寄掉。

双指针

答案必须是连续的区间,所以想到双指针维护区间合法性,但需要处理以下细节:

  • \(a_r\neq a_{r+1}\) 单个数不合法,右移右指针直到成立,此时与之前区间有断开,右移左指针到右指针。

  • \(vis_{a_r} \neq 0\) 说明当前的数区间包含过,右移左指针直到 \(vis_{a_r}= 0\),移除遍历过的数的标记。

  • \(a_r=a_{r+1} \& a_r=a_{r+2}\) 区间必须断开,如果 \(vis_{a_r}=0\) 要先计算答案,右移右指针,右移左指针到右指针。

#include <bits/stdc++.h>
#define int long long
#define re register
const int N=2e5+10;
const int mod=998244353;
using namespace std;
int n;
int a[N];
int vis[N];
int ans=0;
void solve(){
cin>>n;
for(int i=1;i<=n;i++){
cin>>a[i];
}
a[n+1]=-1;
int l=1,r=1;
while(l<=n&&r<=n){
int flag=0;
if(a[r]==a[r+1]&&a[r]==a[r+2]){
if(!vis[a[r+2]]){
ans=max(ans,r+2-l);
}
flag=1;
while(a[r]==a[r+1]&&a[r]==a[r+2]){
r++;
}
}
while(a[r]!=a[r+1]){
flag=1;
r++;
}
if(r>n){
break;
}
if(flag){//有断开标记
while(l<r){
vis[a[l]]=0;
l++;
}
}
if(vis[a[r]]){
while(vis[a[r]]){
vis[a[l]]=0;
l+=2;
}
}
vis[a[r]]=1;
if(a[r]!=a[r+2]){
r+=2;
}
ans=max(ans,r-l);
}
cout<<ans;
}
signed main(){
ios::sync_with_stdio(false);
cin.tie(nullptr);
solve();
return 0;
}

[ABC381E] 11/22 Subsequence

赛后的数据才是这个题的精髓,他和C题一样,只是加了个区间,那么我们就找区间,先把/下标存起来,我就是没存吃大亏,然后二分查找在区间内的下标,然后就遍历计算,这样在赛时是可以过的,赛后就不可以了,我们还可以再二分查找,如果左部分数大于右部分那就缩小右端点,其中不断更新答案最大值。

[ABC381F] 1122 Subsequence

看到数字个数非常少,考虑状压。

我们想要分成并不相交的多个部分,设状态 \(f_s\)\(s\) 状态选出的数中到达最右边的位置,每次从当前状态没有的数字转移,找到 \(f_s\) 右边第一个当前数字的位置 \(now\),再从 \(now\) 右边找到第一个这个数字,这个可以预处理出来。

最后遍历状态如果 \(f_s\le n\) 就说明状态合法,统计选的数字个数。

posted @   sad_lin  阅读(136)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· .NET10 - 预览版1新功能体验(一)
点击右上角即可分享
微信分享提示