LOJ #6065. 「2017 山东一轮集训 Day3」第一题

今天模拟赛考到了这题,因为之前听过两遍还写过很快就写掉了,发现没写过题解来补一发

我们发现六根木棍分组的方案显然只有两种:\(\{1,1,1,3\}\)\(\{1,1,2,2\}\),而这两种互相独立,考虑分别求解

首先考虑前者,容易想到枚举\(1\)的部分,然后计算\(3\)的部分方案数,但这样后面的可能略微有点难算(涉及去重)

考虑我们把木棍排序之后然后从小到大做,每次枚举的是\(3\)的部分的最大值,然后再枚举\(1\)的部分

这样显然剩下的\(2\)的部分一定在当前位置的前面,可以直接开个数组预处理方案数

然后考虑后者,还是先想到枚举\(1\)的部分,然后我们发现要找到两个数之和等于该数

不难发现当我们在排序的数组上处理时,只需要一个two points就可以找出所有的数对了

之后的统计就很简单了,不要忽略两端点重合的情况以及这个数对本身就能拿出两组的情况

大致就是这样了,细节上的问题还是看代码吧

#include<cstdio>
#include<algorithm>
#define int long long
#define RI register int
#define CI const int&
using namespace std;
const int N=5005,S=1e7;
int n,a[N],sum[S+5],t[S+5],ans;
inline int C2(CI x)
{
	return x*(x-1)/2LL;
}
inline int C3(CI x)
{
	return x*(x-1)*(x-2)/6LL;
}
inline int C4(CI x)
{
	return x*(x-1)*(x-2)*(x-3)/24LL;
}
signed main()
{
	RI i,j; for (scanf("%lld",&n),i=1;i<=n;++i) scanf("%lld",&a[i]),++t[a[i]];
	//1 1 1 3
	for (sort(a+1,a+n+1),i=1;i<=n;++i) 
	{
		for (j=i+1;j<=n;++j) if (t[a[j]]>=3) { ans+=sum[a[j]-a[i]]*C3(t[a[j]]); while (j<=n&&a[j]==a[j+1]) ++j; }
		for (j=1;j<i;++j) if (a[i]+a[j]<=S) ++sum[a[i]+a[j]];
	}
	//1 1 2 2
	for (n=unique(a+1,a+n+1)-a-1,i=1;i<=n;++i) if (t[a[i]]>=2)
	{
		int ret=0,cur=0; for (RI l=1,r=i-1;l<=r;++l)
		{
			while (l<=r&&a[l]+a[r]>a[i]) --r; if (l>r||a[l]+a[r]!=a[i]) continue;
			if (l==r) { if (t[a[l]]>=4) ret+=C4(t[a[l]]); if (t[a[l]]>=2) ret+=C2(t[a[l]])*cur; } //important!
			else { if (t[a[l]]>=2&&t[a[r]]>=2) ret+=C2(t[a[l]])*C2(t[a[r]]); ret+=t[a[l]]*t[a[r]]*cur; cur+=t[a[l]]*t[a[r]]; }
		}
		ans+=ret*C2(t[a[i]]);
	}
	return printf("%lld",ans),0;
}
posted @ 2020-07-20 20:30  空気力学の詩  阅读(221)  评论(0)    收藏  举报