LOJ6065「2017 山東一輪集訓 Day3」第一題
阿新 • • 發佈:2021-10-16
考慮 \(6\) 個線段組成正方形,必有 \(2\) 或 \(3\) 條邊是隻由一根線段組成
由於獨立所以可以分別處理這兩種情況
對於 \(3\) 條只由一根線段組成比較簡單,為了防止那一條由三根線段組成的邊計數重複,可以排序以後從小到大列舉 \(i\) 表示這三條線段中最大的那個,再列舉一個 \(L\) 表示那三個只由一根線段組成的邊的長度
維護一個 \(num_x\) 表示有多少個 \(a_i=x\),再維護 \(cnt_x\) 表示有多少對 \(p,q\) 滿足 \(p,q\in [1,i),a_p+a_q=x\)
所以此時對答案的貢獻就是 \(\binom{num_L}{3}cnt_{L-a_i}\)
對於 \(2\) 條邊只由一根線段組成的情況,還是列舉這個 \(L\),但此時若再列舉另一根線段則很難再計算
所以考慮在排序後的數列上尺取,對於每個 \(l\) 每次找到 \(r\)(如果有)使得 \(a_l+a_r=L\),此時如果剩餘兩條邊的情況都是一根 \(a_l\) 和一根 \(a_r\) 組成,那麼對答案的貢獻就是 \(\binom{num_{a_l}}{2}\binom{num{a_r}}{2}\)
另一種可能是一條邊由一根 \(a_l\) 一根 \(a_r\) 組成,另一條邊由之前列舉的某個 \(a_{l'},a_{r'}\) 組成
所以維護一個 \(sum\)
要特殊討論 \(l=r\) 的情況
#define N 5006 #define V 10000006 int n; int a[N]; int cnt[V],num[V]; inline void add(int x){if(x<=1e7) cnt[x]++;} inline long long _2(long long num){return num*(num-1)/2;} inline long long _3(long long num){return num*(num-1)*(num-2)/6;} inline long long _4(long long num){return num*(num-1)*(num-2)*(num-3)/24;} int main(){ // freopen("yist7.in","r",stdin); n=read(); for(int i=1;i<=n;i++) a[i]=read(),num[a[i]]++; std::sort(a+1,a+1+n); add(a[1]+a[2]); long long ans=0; for(int i=3;i<=n;i++){//3 相等 for(int j=i+1;j<=n;j++)if(a[j]^a[j-1]) ans+=_3(num[a[j]])*cnt[a[j]-a[i]]; for(int j=1;j<i;j++) add(a[i]+a[j]); } n=std::unique(a+1,a+1+n)-a-1; for(int i=1;i<=n;i++)if(num[a[i]]>=2){ long long now=0,sum=0; for(int l=1,r=i-1;l<=r;l++){ while(l<=r&&a[l]+a[r]>a[i]) r--; if(a[l]+a[r]!=a[i]||l>r) continue; if(l==r) now+=_4(num[a[l]])+sum*_2(num[a[l]]),sum+=_2(num[a[l]]); else now+=_2(num[a[l]])*_2(num[a[r]])+sum*num[a[l]]*num[a[r]],sum+=num[a[l]]*num[a[r]]; } ans+=now*_2(num[a[i]]); } printf("%lld\n",ans); return 0; }