题意
n个城市,联通图,给出n-1条信息,表示城市A和城市B之间有2*C条无向路相连通。需求出从城市1开始,遍历所有边的方案数。
数据范围:
2 ≤ n ≤ 1e5
路的总数量不超过2*1e6
样例输入:
3
1 2 1
2 3 1
表示3个城市,1-2有2条路,2-3有三条路
题解
由于点数为1e5,静态链表方式存图。
求方案数的过程为深搜。
对于当前节点u来说,它有若干个字节点v1、v2、、、
ans[u]= ( ∑(num[u][vi]) )! * ∏( ( 2*num[u][vi] )! ) / ∏ ( num[u][vi] !) * ∏Lucas(out[vi] + num[u][vi] -1,num[u][vi]-1) * ∏ans[vi];
ps: num[u][vi]表示u->vi有2*num[u][vi]条边
表示这个递推公式让人根本不想看。。T_T
其实要做的只有两件事,遍历当前子节点的方案数,将子树与子树的遍历进行穿插的方案数。
先将每个子树看作一个节点,当前节点u有子节点v1、v2、、、vk,遍历当前子节点的方案数为ans[u] = out(u) ! /∏ ( num[u][vi] !) , out(u) = ∑(num[u][vi]), 为u的出度的一半。
同时,需考虑u-vi之间,边与边的不同:
ans[u] = ans[u] * ∏( ( 2*num[u][vi] )! )
其次,要将u-vi的进出穿插进vi子树本身的遍历中,同插空法,在out[vi]个小球中,插入num[u][vi]-1个隔板,将其分成num[u][vi]份,每一份>=0.
ans[u] = ans[u] * ∏Lucas(num[vi] + num[u][vi] -1,num[u][vi]-1)
最后,将子树的遍历方案数乘进来
ans[u] = ans[u] * ∏ans[vi];
代码
#include <iostream>
#include <cstdio>
#include <cstring>
#include <string>
#include <algorithm>
using namespace std;
typedef long long ll;
#define mod 1000000007
#define p 1000000007
#define LL ll
const ll MAX_N = 1e5+10;
struct EDGE
{
ll b;
ll num;
ll pre;
}edge[MAX_N * 2];
ll point[MAX_N];
ll num[MAX_N];
void add_edge(ll a,ll b,ll c,ll i)
{
edge[i].b = b;
edge[i].num = c;
edge[i].pre = point[a];
point[a] = i;
num[a] = (num[a] + c)%mod;
}
ll half(ll x)
{
ll ans = 1LL;
for(ll t = 2*x; t>x; t--)
{
ans = (ans*t)%mod;
}
return ans;
}
ll full(ll x)
{
ll ans = 1LL;
for(ll t = x; t>0; t--)
{
ans = (ans*t)%mod;
}
return ans;
}
LL quick_mod(LL a, LL b)
{
LL ans = 1;
a %= p;
while(b)
{
if(b & 1)
{
ans = ans * a % p;
b--;
}
b >>= 1;
a = a * a % p;
}
return ans;
}
LL C(LL n, LL m)
{
if(m > n) return 0;
LL ans = 1;
for(int i=1; i<=m; i++)
{
LL a = (n + i - m) % p;
LL b = i % p;
ans = ans * (a * quick_mod(b, p-2) % p) % p;
}
return ans;
}
LL Lucas(LL n, LL m)
{
if(m == 0) return 1;
return C(n % p, m % p) * Lucas(n / p, m / p) % p;
}
ll dfs(ll x, ll last, ll dec)
{
num[x] -= dec;
ll ans = 0LL;
ll add = 0LL;
ll mul = 1LL;
for(ll t = point[x];t!=-1;t = edge[t].pre)
{
ll v = edge[t].b;
if(v == last) continue;
add = (add + edge[t].num) % mod;
mul = (mul * half(edge[t].num)) % mod;
}
add = full(add);
ans = (add * mul)%mod;
for(ll t = point[x];t!=-1;t = edge[t].pre)
{
ll v = edge[t].b;
if(v == last) continue;
ans = (ans * dfs(v, x, edge[t].num)) % mod;
ll tans = 0LL;
tans = (tans + Lucas( edge[t].num + num[v]-1, edge[t].num-1)) % mod;
ans = (ans * tans) % mod;
}
return ans;
}
int main()
{
ll n;
while(scanf("%lld",&n)!=EOF)
{
memset(point,-1,sizeof(point));
memset(num,0,sizeof(num));
for(int i=1;i<n;i++)
{
ll a,b,c;
scanf("%lld%lld%lld",&a,&b,&c);
add_edge(a,b,c,i);
add_edge(b,a,c,i+n-1);
}
ll ans = dfs(1,-1,0);;
printf("%lld\n",ans);
}
return 0;
}
其中lucas的模版来源于ACdreamers的博客组合数取模

本文介绍了一种计算从指定起点遍历所有边的方案数的算法。针对大规模图(最多10万节点),使用静态链表存储图结构,并通过深度优先搜索计算方案数。文章详细解释了递推公式的含义及实现过程,并提供了完整的C++代码。

2228

被折叠的 条评论
为什么被折叠?



