LOJ #6065. 「2017 山東一輪集訓 Day3」第一題
阿新 • • 發佈:2020-07-20
今天模擬賽考到了這題,因為之前聽過兩遍還寫過很快就寫掉了,發現沒寫過題解來補一發
我們發現六根木棍分組的方案顯然只有兩種:\(\{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; }