CF1400D 【Zigzags】

翻译

给定一个长度为 \(n\) 的序列 \(a\) \(( n\le 3000,a_i \le n )\) ,求有多少个四元组 \((i,j,k,l)\) 满足:

  • \(1 \le i < j < k < l \le n\)
  • \(a_i=a_k\)\(a_j=a_l\)

思路

看到 \(n \le 3000\) ,所以可以确定复杂度最高是 \(\text{O}(n^2)\) 的,故枚举时至多只能枚举两个变量。

那么枚举 \(i,j,k,l\) 中的哪两个量能得到的信息最多呢?

  • 当枚举 \(i,j\) 时:

    能够得到满足条件时 \(a_k\)\(a_l\) 的值,即 \(a_k=a_i\)\(a_l=a_j\)

    但是这样枚举无法得到 \(k,l\) 的相对位置,不好满足条件 \(k<l\),故不可行。

    枚举 \(k,l\) 也是同理。

  • 当枚举 \(i,k\) 时:

    能够得到 \(j,l\) 的相对位置,即 \(j\)\(i\sim k\) 之间, \(l\)\(k \sim n\) 之间。

    但是这样无法得到 \(a_j\)\(a_l\) 的值,不好满足条件 \(a_j=a_l\) ,故也不可行。

    枚举 \(j,l\) 也是同理。

  • 当枚举 \(j,k\) 时:

    能够得到满足条件时 \(a_i\)\(a_l\) 的值,即 \(a_i=a_k\)\(a_l=a_j\)

    同时也能够得到 \(i,l\) 的相对位置,即 \(i\)\(1\sim j\) 之间, \(l\)\(k \sim n\) 之间。

    这显然是可行的。

那么我们可以枚举 \(j,k\),这样只要统计一个前缀和,维护每个数在某个区间出现的个数,再根据乘法原理,就能 \(\text{O}(1)\) 计算出当 \(j,k\) 确定时的方案数。

由于 \(a_i \le n\) ,故预处理前缀和的复杂度也是 \(\text{O}(n^2)\) 的。

总时间复杂度 \(\text{O}(n^2)\)

Code

#include<bits/stdc++.h>
#define int long long

using namespace std;

int read()
{
	int ans=0,f=1;
	char c=getchar();
	while(c>'9'||c<'0'){if(c=='-')f=-1;c=getchar();}
	while(c>='0'&&c<='9'){ans=ans*10+c-'0';c=getchar();}
	return ans*f;
}

const int N=3005;
int t,n,sum[N][N],a[N],ans;

signed main()
{
	t=read();
	while(t--)
	{
		n=read();
		for(int i=1;i<=n;++i)
		{
			a[i]=read();
			for(int j=1;j<=n;++j)
				sum[i][j]=sum[i-1][j];
			sum[i][a[i]]++;
			//统计每个数出现次数的前缀和 
		}
		ans=0;
		
		//枚举 j,k O(1) 计算答案 
		for(int j=2;j<=n;++j)
			for(int k=j+1;k<=n-1;++k)
				ans+=(sum[j-1][a[k]])*(sum[n][a[j]]-sum[k][a[j]]);
		printf("%lld\n",ans);
	}
	return 0;
}
© 版权声明 文章版权归作者所有,未经允许请勿转载。
posted @ 2020-08-26 12:23  Blackbird137  阅读(202)  评论(1编辑  收藏  举报