BZOJ 4927 第一题

题目链接

https://www.lydsy.com/JudgeOnline/problem.php?id=4927

题解

6个木棍拼成一个正方形,只有下面两种情况:

picture1

第一种情况

排序,先枚举蓝色边,再枚举红色边,那么绿色边+黄色边的值已经确定了,记 s u m [ i ] sum[i] sum[i]表示两条边之和为 i i i的方案数,对于每个枚举到的蓝色边和红色边,加入答案即可。 s u m sum sum可以由蓝色边更新。

第二种情况

排序去重,先枚举红色边,再枚举蓝色边,记黄色边的长度大于蓝色边长度时,黄色边和天蓝色边的构成方案数为 p a s t past past,已知蓝色边,为了不重复枚举,要么由一条长度为蓝色和一条 p a s t past past中统计过的边构成,要么由两条长度为蓝色的边构成。

细节比较麻烦,需要想清楚。

代码

#include <cstdio>
#include <algorithm>
 
int read()
{
  int x=0,f=1;
  char ch=getchar();
  while((ch<'0')||(ch>'9'))
    {
      if(ch=='-')
        {
          f=-f;
        }
      ch=getchar();
    }
  while((ch>='0')&&(ch<='9'))
    {
      x=x*10+ch-'0';
      ch=getchar();
    }
  return x*f;
}
 
const int maxn=5000;
const int maxv=10000000;
 
int n,v[maxn+10],f[maxn+10],cnt[maxn+10],tot,num[maxv+10],sum[maxv+10];
long long ans;
 
long long C(int x,int y)
{
  if(x<y)
    {
      return 0ll;
    }
  long long res=x;
  for(int i=2; i<=y; ++i)
    {
      res=res*(x-i+1)/i;
    }
  return res;
}
 
int main()
{
  n=read();
  for(int i=1; i<=n; ++i)
    {
      v[i]=read();
    }
  std::sort(v+1,v+n+1);
  for(int i=1; i<=n; ++i)
    {
      ++num[v[i]];
    }
  for(int i=3; i<n; ++i)
    {
      for(int j=1; j<i-1; ++j)
        {
          if(v[i-1]+v[j]<=maxv)
            {
              ++sum[v[i-1]+v[j]];
            }
        }
      int now=i+1;
      while(now<=n)
        {
          if(num[v[now]]>=3)
            {
              ans+=1ll*sum[v[now]-v[i]]*C(num[v[now]],3);
            }
          while(v[now+1]==v[now])
            {
              ++now;
            }
          ++now;
        }
    }
  tot=std::unique(v+1,v+n+1)-v-1;
  for(int i=2; i<=tot; ++i)
    {
      long long past=0;
      int fuckpps=num[v[i]]*(num[v[i]]-1)/2;
      for(int j=i-1; (j>0)&&(v[j]*2>=v[i]); --j)
        {
          ans+=1ll*past*((v[j]*2!=v[i])?(1ll*num[v[j]]*num[v[i]-v[j]]):(1ll*num[v[j]]*(num[v[j]]-1)/2))*fuckpps;
          if(v[j]*2!=v[i])
            {
              ans+=1ll*num[v[j]]*num[v[i]-v[j]]*(num[v[j]]-1)*(num[v[i]-v[j]]-1)/4*fuckpps;
            }
          else
            {
              ans+=1ll*C(num[v[j]],4)*fuckpps;
            }
          past+=1ll*num[v[j]]*num[v[i]-v[j]];
        }
    }
  printf("%lld\n",ans);
  return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值