1. 程式人生 > >2018.12.08【NOIP提高組】模擬B組 5123. diyiti

2018.12.08【NOIP提高組】模擬B組 5123. diyiti

(File IO): input:yist.in output:yist.out
Time Limits: 2000 ms Memory Limits: 524288 KB Detailed Limits Downloads

Description
給定n 根直的木棍,要從中選出6 根木棍,滿足:能用這6 根木棍拼出一個正方形。注意木棍不能彎折。問方案數。
正方形:四條邊都相等、四個角都是直角的四邊形。

Input
第一行一個整數n。
第二行包含n 個整數ai,代表每根木棍的長度。

Output
一行一個整數,代表方案數。

Sample Input
8
4 5 1 5 1 9 4 5

Sample Output
3

Data Constraint
對於20% 的資料,滿足:n ≤ 30
對於40% 的資料,滿足:n ≤ 200
對於60% 的資料,滿足:n ≤ 1000
對於100% 的資料,滿足:n ≤ 5000; 1 ≤ ai ≤ 10^7


思路: 1.兩種情況分類討論

  • (1,1,2,2)

先將a陣列去重變為b,b對應這個數值出現的次數num
列舉i,最長的兩條(1,1)在1~i-1這個區間找合法的兩對,這兩對的和相加都為b[i]
ans+=C(num[i],2)*C(合法對數,2);
可以用兩個指標l,r不斷往中間指,同時記錄合法對數(組

  • (1,1,1,3)

不能n^3列舉,怎麼辦呢?
i :1->n 列舉三個短邊中最長的一條,j : i+1 - > n列舉最長的那三條(1,1,1)
這樣就不怕重複了
假如我們有f[x]為到目前元素兩兩配對和為x的方案數
則ans+=C(num[j],3)*s[b[j]-a[i]];(邊長為b[j],已經確定了一條邊i,還剩b[j]-a[i]要湊)

#include<bits/stdc++.h>
#define ll long long
#define N 5010
#define MAX (ll)(1e7)
#define open(x) freopen(x".in","r",stdin);freopen(x".out","w",stdout);
using namespace std;

ll n,m,l,r,i,j,ans,cnt,sum;
ll a[N],b[N],num[N],id[N],s[MAX];

int main()
{
	open("yist");
	scanf("%lld",&n);
	for(i=1;i<=n;i++)scanf("%lld",&a[i]);
	sort(a+1,a+1+n);	
	for(i=1;i<=n;i++)
	{
		if(a[i]!=a[i-1])b[++m]=a[i];
		++num[m];
		id[i]=m;
	}
	for(i=1;i<=m;i++)
	{
		if(num[i]>=2)
		{	sum=cnt=0;
			for(l=1,r=i-1;l<=r;l++)
			{
				while(l<=r && b[l]+b[r]>b[i])--r;
				if(l>r|| b[l]+b[r]!=b[i])continue;
				if(l<r)
				{
					if(num[l] >=2 && num[r]>=2)cnt+=(num[l])*(num[l]-1)/2*(num[r])*(num[r]-1)/2;
					cnt+=num[l]*num[r]*sum;
					sum+=num[l]*num[r];
				}
				else
				{
					if(num[l]>=4)cnt+=num[l]*(num[l]-1)*(num[l]-2)*(num[l]-3)/24;
					cnt+=num[l]*(num[l]-1)/2*sum;
					
				}
			}
			ans+=num[i]*(num[i]-1)/2*cnt;
		}
	}
	memset(s,0,sizeof s);
	for(i=1;i<=n;i++)
	{
		for(j=id[i]+1;j<=m;j++)if(num[j]>=3)ans+=num[j]*(num[j]-1)*(num[j]-2)/6*s[b[j]-a[i]];
		for(j=1;j<i;j++)if(a[i]+a[j]<a[n])++s[a[i]+a[j]];
	}
	printf("%lld",ans);
}