1. 程式人生 > 其它 >浙大集訓Day-10:A.Sequence(雙指標)

浙大集訓Day-10:A.Sequence(雙指標)

浙大集訓Day-10:A.Sequence

我們定義一個序列\(a\),值域\([1,k]\)是好的,當且僅當對於\(1\)\(k\)的每一個數\(j\),在序列裡的出現次數都在\([l_j,r_j]\)之間。

詢問有多少個這樣的子串,是好的。

#include<bits/stdc++.h>
using namespace std;
const int maxn=2e5+100;
int n,k,a[maxn];
int l[maxn],r[maxn];
int cnt[maxn];
int cc=0;
int p1[maxn],p2[maxn];
int main () {
	scanf("%d%d",&n,&k);
	for (int i=1;i<=n;i++) scanf("%d",a+i);
	for (int i=1;i<=k;i++) scanf("%d%d",l+i,r+i);
	int L=1,R=1;
	//先求每個點左邊第一個滿足的點 
	cnt[a[1]]=1;
	for (int i=1;i<=k;i++) if (cnt[i]>=l[i]) cc++;
	while (1) {
		while (R<n) {
			if (cc==k&&R>=L) {
				//如果已經全部滿足,就不用了 
				break;
			}
			else {
				R++;
				cnt[a[R]]++;
				if (cnt[a[R]]==l[a[R]]) cc++;
			}
		}
		if (cc==k) p1[L]=R;
		cnt[a[L]]--;
		if (cnt[a[L]]==l[a[L]]-1) cc--;//如果第一次突破下界,cc-- 
		L++;
		if (L>n) break;
	} 
	
	L=1,R=1;
	for (int i=1;i<=k;i++) cnt[i]=0;
	cnt[a[1]]=1;
	cc=0;
	for (int i=1;i<=k;i++) if (cnt[i]>r[i]) cc++;
	while (1) {
		while (R<n) {
			if (cnt[a[R+1]]+1<=r[a[R+1]]) { 
				R++;
				cnt[a[R]]++;
			}
			else {
				break;
			}
		}
		if (cc==0) p2[L]=R;
		cnt[a[L]]--;
		if (cnt[a[L]]==r[a[L]]) cc--;//如果第一次回到上界,cc-- 
		L++;
		if (L>n) break;
	}
	long long ans=0;
	for (int i=1;i<=n;i++) {
		if (p1[i]>p2[i]) continue;
		if (!p1[i]||!p2[i]) continue;
		//printf("%d %d %d\n",i,p1[i],p2[i]);
		ans+=p2[i]-p1[i]+1;
	}
	printf("%lld\n",ans);
	
}