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 @ 2024-11-23 10:06  sad_lin  阅读(81)  评论(0编辑  收藏  举报