原题解传送门
感谢大佬的博客教会我解这道题.不过我觉得推式子的过程可以再简化一下.
题目大意
在一个D维的空间里,每一维的坐标轴都有一条平行于它的线段.这个线段的两个端点,除了这一维的坐标不同之外,其余维度的坐标均相同。两个端点在每一维的坐标都是[0,n-1]之间的随机整数,且两个端点不重合.两条线段交于一点时,XXX会获得一点能量,问:XXX获得的能量值的期望是多少?
输入
多组数据,每组一行,两个数,分别为n,D;
输出
每组一行,一个既约分数p/q。如果分母为1,则只输出分子。
数据范围
1<n≤109,D≤99,数据组数≤10
题解
其实就是一个计数问题,主要难点在于相交情况的统计.
首先,每条线段都是平行于某一坐标轴的,那么两条相交线段最多只能有两维的坐标是不同的.不妨设这两维是X,Y;
考虑一条平行于X轴(或Y轴)的线段有多少种可能呢?
n⋅C2n=n2(n−1)2
那么两条线段组合起来的情况就有:
n4(n−1)24
种。
那么相交的情况呢?设两条线段的交点为
(i,j)
,两条线段分别为a,b其中a平行于x轴,b平行于y轴.
那么由于
a.start.x<=i,a.end.x>=i,a.start.x!=a.end.x且a.y=j
所以a的可能情况就有
(n−i)(i+1)−1=(i+1)n−(i2+i+1)
种.
b的情况同理.
于是相交的情况:
所以对于一组(X,Y)出现交点的概率为:
所以期望值:
题目要求输出分数,但是分子分母都会爆long long,于是需要用高精度计算分子分母。鉴于高精度约分不方便,我们可以把这些因式先约分至最简,然后再乘起来.
Code
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
#define MAXN 1200
struct big
{
int x[MAXN],len;
big()
{
memset(x,0,sizeof(x));
len=0;
}
big operator = (char* s)
{
len=strlen(s);
for(int i=1;i<=len;i++)
{
x[i]=s[len-i]-'0';
}
return *this;
}
big operator = (int num)
{
char s[MAXN];
sprintf(s,"%d",num);
return *this=s;
}
void print()
{
for(int i=len;i>=1;i--)
{
printf("%d",x[i]);
}
return ;
}
big operator * (const big& b) const
{
big c=big();
c.len=len+b.len;
for(int i=1;i<=len;i++)
{
for(int j=1;j<=b.len;j++)
{
c.x[i+j-1]+=x[i]*b.x[j];
}
}
for(int i=1;i<=c.len;i++)
{
c.x[i+1]+=c.x[i]/10;
c.x[i]%=10;
}
for(;c.len>1&&!c.x[c.len];c.len--);
return c;
}
};
int n,d;
inline int gcd(int x,int y)
{
int t;
while(y)
{
t=x;
x=y;
y=t%y;
}
return x;
}
int A[MAXN],B[MAXN],flag;
int main()
{
while(scanf("%d%d",&n,&d)==2)
{
A[1]=d;
A[2]=d-1;
A[3]=A[4]=n+4;//分子
B[1]=18;
for(int i=2;i<=d+1;i++)
{
B[i]=n;
}//分母
for(int i=1;i<=4;i++)
{
for(int j=1,tt;j<=d+1&&A[i]>1;j++)
{
if(B[j]>1)
{
tt=gcd(A[i],B[j]);
A[i]/=tt;
B[j]/=tt;
}
}
}//因为数据很小直接暴力约分,有些题解用的分解质因数
big p,q;
p=1;
for(int i=1;i<=4;i++)
{
q=A[i];
p=p*q;
}
p.print();
p=1;
flag=1;
for(int i=1;i<=d+1;i++)
{
q=B[i];
p=p*q;
if(B[i]>1)
{
flag=0;
}
}
if(!flag)
{
printf("/");
p.print();
}
putchar('\n');
}
return 0;
}