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;
}
辣鸡老年选手AFO在即