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

猜你喜欢

转载自blog.csdn.net/Com_man_der/article/details/84900065