F.双生双宿之探

题目链接:https://ac.nowcoder.com/acm/contest/95323/F

题意:

给定一个长度为n的数组a,规定若其一个子数组 长度为偶数 并且数组内 有两个不相同的数字 且 数量相同 ,则该数组为双生数组

求数组a中有多少个双生数组

思路:

纯暴力枚举每一个区间显然复杂度O(nnn)

考虑到 从左往右扩展区间 若区间数字种类超过两个便停止 ,发现此区间的下一个区间,一定不会与拥有 此区间最左边的数字 的区间 相交,而只会最大程度上与 拥有 此区间最右边的数字 的区间 相交

那么一左一右 就可以用双指针 枚举代价变为O(n)

当确定了区间之后,就需要统计此区间双生数组的数目

如何统计?既然要让两种数字的数目相同,不妨离散化,让一种数字变为1,另一种数字变为-1。这样 若 子区间的区间和为0,说明子区间两种数字数目相同,就说明这是一个双生数组

如何确定子区间区间和?显然用前缀和解决,既然 presum[x]-presum[y-1]=0 证明[x,y]为一个双生数组 那么只要看presum[x]=presum[y-1]

用set保证两种数字,map存储从l~r的前缀和,额外复杂度为O(logn)

值得注意的是本来寻思着双指针复杂度是O(n),然后前缀遍历一遍复杂度看着也像O(n)那搞里头不应该变成O(nnlogn)级别的了吗》

后来想想发现双指针扩的区域越大,双指针遍历的就越少,前缀遍历的就越多;双指针扩的区域越小,双指针遍历的就越多,前缀遍历的就越少

所以两个乘起来是O(n)的~

#include<bits/stdc++.h>
#define rep(i,a,n) for(int i=a;i<=n;i++)
#define pb push_back
#define int long long
#define endl "\n"
#pragma GCC optimize(3)
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
const int inf=0x3f3f3f3f;
const ll llmax=LLONG_MAX;
const int maxn=1e5+5;
const int mod=1e9+7;
int n;
int a[maxn];

void solve(){
	cin>>n;

	int ans=0;
	rep(i,1,n) cin>>a[i];
	
	
	for(int l=1;l<=n;){
		map<int,int>mp;
		set<int>st;
		int r=l+1;
		
		st.insert(a[l]);
		
		
		while(st.size()<=2&&r<=n){
			st.insert(a[r]);
			if(st.size()==3){
				st.erase(a[r]);r--;break;
			}
			r++;
		}
		
		if(r>n)r=n;
		if(st.size()==1)break;
		
		
		mp[0]=1;
		int s=0;
		rep(i,l,r){
			s+=(a[i]==a[l]?1:-1);
			ans+=mp[s];
			mp[s]++;
//			cout<<"s "<<s<<endl;
		}
//		rep(i,1,n){
//			cout<<a[i]<<endl;
//		}
//		for(auto it:mp){
//			cout<<it.first<<' '<<it.second<<endl;
//		}
		while(r-1>=l&&a[r-1]==a[r])r--;
		l=r;
	}
	
	cout<<ans<<endl;
}
signed main()
{
	ios::sync_with_stdio(false),cin.tie(0);
	int T;cin>>T;
	while(T--){
		solve();
	}	
	return 0;
}


posted @   Marinaco  阅读(2)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· winform 绘制太阳,地球,月球 运作规律
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
点击右上角即可分享
微信分享提示