2019.6.6 提高A组 T2 JZOJ 4804【NOIP2016提高A组模拟9.28】成绩调研

D e s c r i p t i o n Description Description

给定一个长度为 n n n的序列,现在从中选出一个连续的区间,使得这个区间内的每种数的范围在给出的 k k k个限制以内

数据范围:
。。。


S o l u t i o n Solution Solution

前缀和 O ( n 2 k ) O(n^2k) O(n2k)暴力就有60分。。。

#include
#include
#include
#include 
#define N 2001
using namespace std;int n,a[N*100],k,t[N][N],l[N*100],r[N*100],ans;
inline bool check(int x,int y)
{
	for(register int i=1;i<=k;i++) if(t[y][i]-t[x-1][i]<l[i]||t[y][i]-t[x-1][i]>r[i]) return false;
	return true;
}
inline int read()
{
    int f=0,d=1;char c;
    while(c=getchar(),!isdigit(c)) if(c=='-') d=-1;f=(f<<3)+(f<<1)+c-48;
    while(c=getchar(),isdigit(c)) f=(f<<3)+(f<<1)+c-48;
    return d*f;
}
signed main()
{
	freopen("survey.in","r",stdin);
	freopen("survey.out","w",stdout);
	n=read();k=read();
	for(register int i=1;i<=n;i++) 
	{
		a[i]=read();
		for(register int j=1;j<=k;j++) t[i][j]=t[i-1][j];
		t[i][a[i]]++;
	}
	for(register int i=1;i<=k;i++) l[i]=read(),r[i]=read();
	for(register int i=1;i<n;i++)
	 for(register int j=i+1;j<=n;j++)
	  if(check(i,j)) ans++;
	printf("%d",ans);
}

正解是指针扫描

固定一个右区间,那么符合答案的左区间一定也是一个连续的区间 [ l 1 , l 2 ] [l_1,l_2] [l1,l2],当数超过了范围,将 l 1 l_1 l1往后挪,当 l 1 l_1 l1 l 2 l_2 l2满足要求时,尽量将 l 2 l_2 l2往后挪,这样对答案贡献更多,此时结束时 l 2 l_2 l2正好不合法,而 l 2 − 1 l_2-1 l21恰好合法,累积方案数 ( l 2 − 1 ) − l − 1 + 1 = l 2 − l 1 (l_2-1)-l-1+1=l_2-l_1 (l21)l1+1=l2l1

时间复杂度 O ( n ) O(n) O(n)


C o d e Code Code

#include
#include
#define N 200010
using namespace std;int n,m,l1,l2,r,a[N],L[N],R[N],t1[N],t2[N],sum;
long long ans;
inline int read()
{
    char c;int f=0,d=1;
    while(c=getchar(),!isdigit(c)) if(c=='-') d=-1;f=(f<<3)+(f<<1)+c-48;
    while(c=getchar(),isdigit(c)) f=(f<<3)+(f<<1)+c-48;
    return d*f;
}
signed main()
{
	freopen("survey.in","r",stdin);
	freopen("survey.out","w",stdout);
	n=read();m=read();
	for(register int i=1;i<=n;i++) a[i]=read();
	for(register int i=1;i<=m;i++) L[i]=read(),R[i]=read(),sum+=!L[i];
	l1=l2=1;
	for(register int r=1;r<=n;r++)
	{
		t1[a[r]]++;t2[a[r]]++;//新添加一个数
		if(t2[a[r]]==L[a[r]]) sum++;//进入范围内,合法的种数+1
		while(t1[a[r]]>R[a[r]]) --t1[a[l1++]];//超出范围将对头的数去掉
		while(sum==m&&l2<=r)//若此时所有的要求都满足,则我们尽量把尾指针往后挪,挪到不合法位置
		{
			--t2[a[l2]];//踢出
			if(t2[a[l2]]==L[a[l2]]-1) sum--;//正好少一个
			l2++;//往后挪
		}
		if(l2>l1) ans+=(l2-l1);//此时l1到l2-1就是一段合法的区间
	}
	printf("%lld",ans);
}

你可能感兴趣的:(扫描)