1. 程式人生 > 實用技巧 >LOJ #6065. 「2017 山東一輪集訓 Day3」第一題

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;
}