翻译后的题意
给你n个点,每个点有且只有一条有向边边
你可以在上面选择k个点,问你选到这k个点,沿着边走,可以走完整个图的概率是多少
题解
很明显,这个图,最后会成为很多个不同的联通块
然后对于每一个联通块,我们缩点之后,就会出现一个树,明显地,树的根是一定要选的
当然,树的根有可能是一个环
然而环里面,你任意选择一个,就可以将环遍历完了
所以,我们可以考虑用
i
个节点,把每一个根都选到,有多少种方案,然后剩下的
于是问题就转变成了,现在给你i个选择机会,有n个盒子,每个盒子里面有
ai
个球,问你每个盒子至少选择一个球,有多少种方案
这个的话,怒想了一波排列组合无果
于是就只好用DP了
f[i][j]
表示,前i个盒子,选了j个球,都合法了,有多少种情况
转移也很容易,我就不再赘述了
然后就做完了。。
DP完之后,在枚举一下没多少给根,然后排列组合乘一下就可以了
其他的话
打到一半,我才发现,会炸精度。。
于是有想了半天。。
于是好奇题解有没有排列组合。。
发现他居然直接用double来存。。
smg,300的排列组合居然不怕炸精度?
于是我也试了一下,居然就过了QAQ
看来还是我太年轻了啊
CODE:
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<iostream>
using namespace std;
const int N=305;
int T;
struct qq
{
int x,y,last;
}e[N];
int num,last[N];
int n,k;
void init (int x,int y)
{
num++;
e[num].x=x;e[num].y=y;
e[num].last=last[x];
last[x]=num;
}
int dfn[N],low[N],sta[N],belong[N],TT[N];
bool in[N];
int lalal,cnt,shen;
void dfs (int x)
{
dfn[x]=low[x]=++lalal;
sta[++cnt]=x;
in[x]=true;
for (int u=last[x];u!=-1;u=e[u].last)
{
int y=e[u].y;
if (dfn[y]==-1)
{
dfs(y);
low[x]=min(low[x],low[y]);
}
else if (in[y]) low[x]=min(dfn[y],low[x]);
}
if (dfn[x]==low[x])
{
shen++;TT[shen]=0;
int now;
do
{
now=sta[cnt--];
belong[now]=shen;
TT[shen]++;
in[now]=false;
}while (now!=x);
}
}
int d[N];
int a[N],tot;//这些必选的盒子里面,有多少个人
int sum;//剩下的有多少个
double f[N][N];//前i个盒子 选了j个,有多少种方案
double c[N][N];
void solve ()
{
memset(f,0,sizeof(f));
f[0][0]=1;
for (int u=1;u<=tot;u++)
for (int i=u;i<=n;i++)//一共是多少个
{
for (int j=1;j<=min(i,a[u]);j++)//我这里用多少个
f[u][i]=f[u][i]+f[u-1][i-j]*c[a[u]][j];
}
double ans=0;
for (int u=1;u<=k;u++)//给他多少
ans=ans+f[tot][u]*c[sum][k-u];
printf("%.9lf\n",ans/c[n][k]);
}
int main()
{
scanf("%d",&T);
c[0][0]=1;
for (int u=1;u<=300;u++)
{
c[u][0]=c[u][1]=1;
for (int i=1;i<=u;i++)
c[u][i]=c[u-1][i-1]+c[u-1][i];
}
while (T--)
{
num=0;memset(last,-1,sizeof(last));
scanf("%d%d",&n,&k);
for (int u=1;u<=n;u++)
{
int x;
scanf("%d",&x);
init(u,x);
}
lalal=0;cnt=0;shen=0;
memset(dfn,-1,sizeof(dfn));
memset(in,false,sizeof(in));
for (int u=1;u<=n;u++)
if (dfn[u]==-1)
dfs(u);
memset(d,0,sizeof(d));
for (int u=1;u<=num;u++)
{
int x=e[u].x,y=e[u].y;
if (belong[x]==belong[y]) continue;
d[belong[y]]++;
}
tot=0;
sum=n;
for (int u=1;u<=shen;u++)
if (d[u]==0)
{
a[++tot]=TT[u];
sum=sum-TT[u];//剩下的有这么多个
}
solve();
}
return 0;
}