bnuoj 52305 Around the World dfs+排列组合

来源:互联网 发布:亚马逊关键词优化方法 编辑:程序博客网 时间:2024/06/10 17:40

题意

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 llconst 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的博客组合数取模

0 0