您的位置:首页 > 其它

中国(北方)大学生程序设计训练赛(第一周)-A(生成树计数)

2017-03-06 20:15 323 查看
题目链接:A

根据观察,一个结点出现在Prufer序列中,当且仅当它是非叶子结点。

所以如果我们要计算一个结点对答案的贡献,就要算出该结点是非叶子节点的树的个数乘以改结点的值。

对于一个结点 u ,要计算出其非叶子结点生成树个数,可以计算出其是叶子结点的生成树个数,可以用以下算法:

求点 i 为叶子的生成树个数 f(i) : 

先将图中所有与 i 相邻的边删去,且将点 i 删去。点 i 的度数为 d(i) 。

求出新图中生成树的个数 f(G) ,那么 f(i)=f(G)∗d(i) 。

生成树计数可以直接百度模板。

#include<bits/stdc++.h>
#define pr pair<int,int>
#define mp make_pair
#define fi first
#define se second
using namespace std;
typedef long long ll;
const ll mod=1e9+7;
pr p[10007];
ll C[107][107];
int d[107];
ll det(int n)
{
ll res=1;
for(int i=0;i<n;i++)
{
for(int j=i+1;j<n;j++)
{
while(C[j][i])
{
ll t=C[i][i]/C[j][i];
for(int k=i;k<n;k++)
{
C[i][k]=(C[i][k]-t*C[j][k])%mod;
swap(C[i][k],C[j][k]);
}
res=-res;
}
}
if(C[i][i]==0)  return 0;
res=res*C[i][i]%mod;
}
return (res+mod)%mod;
}
ll cal(int no,int n,int m)
{
memset(C,0,sizeof(C));
for(int i=0;i<m;i++)
{
int u=p[i].fi,v=p[i].se;
if(u!=no&&v!=no)
{
if(u>no) --u;
if(v>no) --v;
C[u][v]=C[v][u]=-1;
++C[u][u];++C[v][v];
}
}
return det(n-1);
}
int main()
{
int n,m;
int T;
scanf("%d",&T);
while(T--)
{
memset(d,0,sizeof(d));
scanf("%d%d",&n,&m);
int u,v;
for(int i=0;i<m;i++)
{
scanf("%d%d",&u,&v);
p[i].fi=--u;p[i].se=--v;
d[u]++;d[v]++;

}
ll ans=0;
ll tnum=cal(200,n,m);
for(int i=0;i<n;i++)
{
ll t=cal(i,n-1,m);
ans=(ans+(i+1)*(tnum-t*d[i]%mod+mod)%mod)%mod;
}
printf("%lld\n",ans);
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐