题解 P3799 【妖梦拼木棒】

· · 题解

这道题纯暴力是不行的,需要结合组合数学求解。

思路:

4根木棒组合成正三角形,那么一定是2个小木棒组成其中一边,另外两边是两个相等的大木棒

把2个小木棒看成一个整体,和大木棒进行组合数求解即可。

首先

我们需要两个桶:x[i]表示用2个小木棒(小是相对的)组合成长度为i整体组合种类数y[i]表示长度为i木棒数目

然后

题目会给出y[i],怎么求x[i]呢?

举个例子:x[4]=y[1]*y[3]+C_{y[2]}^2,其中(y[2]\geq 2)

从长度为1的木棍中抽出一个,从长度为3的木棍中抽出一个,就可以组成长度为4的木棍,根据乘法原理相乘即可,然后加上从长度为2的木棍中抽出两个的种类数。

·两个**不同**木棍组合,$x[i+j]$+=$y[i]*y[j]$。其中$i<j$,避免重复计数; ·两个**等长**木棍组合,$x[2*i]$+=$C_{y[i]}^2$。其中$(y[i]\geq 2)$。 #### 最后 那么长度为$i$时,组合数即:从长度为$i$的木棍中抽出两个的种类数乘以$x[i]$,也就是$sum_i=C_{y[i]}^2*x[i]=\frac{y[i]*(y[i]-1)}{2}*x[i]$,其中$(y[i]\geq 2)$。 **枚举**长度$i$,计数得出结果。(**记得取模!**) ## 代码: ```cpp #include<cstdio> inline short read()//快读 { register short a=0;register char c=getchar(); while(c<'0'||c>'9')c=getchar(); while(c>='0'&&c<='9')a=a*10+(c^48),c=getchar(); return a; } int x[5001],y[5001]; //x[i]表示两根小棍子总长度为i的组合数目 //y[i]表示一根棍子长度为i的数目 //两根棍子组合长度超过5000不需要记录 int main() { int n;register int i,j,sum=0; scanf("%d",&n); for(i=0;i<n;i++)y[read()]++; //将棍子长度的数据装入桶里 for(i=1;i<=2500;i++) for(j=i+1;j<=5000-i;j++) x[i+j]+=y[i]*y[j]; //计算两个不等木棍的组合数目 for(i=1;i<=2500;i++) if(y[i]>1) x[2*i]+=y[i]*(y[i]-1)/2; //加上两个等长木棍的组合数目 for(i=1;i<=5000;i++) if(y[i]>1) sum+=y[i]*(y[i]-1)/2*x[i],sum%=1000000007; //求组合数,sum=y*(y-1)/2*x,还要记得取模! printf("%d",sum); } //PS:吸氧跑得还可以,100+ms ```