ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

树上M乘积路径数

2022-07-26 16:06:02  阅读:154  来源: 互联网

标签:include return 乘积 ll 路径 sums ans 树上 MOD


此题是在某个的oj上看到的,题目标题和数据都不记得了,只能说个大概(标题瞎编的)

题目大意

给出\(n\)个点,可以任意选择生成树和\([1,m]\)的边权。由这些点连成一棵树,树中每一条边的权重介于\([1,m]\)给出两点\(a,b\)问有多少种树满足\(a,b\)之间的路径的权值乘积为\(m\)

数据范围

\(n<=10^{6}\)(应该是),\(m<=10^{18}\)

题目解析

这里有道简化版的类似题目:CodeForces #539 (Div.2) F:Sasha and Interesting Fact from Graph Theory

首先此题需要用到凯利公式(原题提示中已给出):$$设T_{n,k}为n个有标号点分成k个连通块的的分割种数,其中节点1,2,3,…k属于不同的联通块,则有
T_{n,k}=k\ n^{n-k-1}$$

与上面简化版类似,假设给定的\(a,b\)之间假设存在条路径,总共有\(i\)条边,\(i+1\)个点,那么除了\(a,b\)选择剩下的节点方案数为\(C_{n-2}^{i-1}\),乘以对点进行全排列\(A_{i-1}\)。

满足ab路径的边权条件后,其他边权的分配方案为\(m^{n-1-i}\)。

然后将剩下的\(n-i-1\)个节点接入\(a,b\)的路径,除去\(a,b\)路径上的边,就转化为了将\(n\)个点划分到\(i+1\)个联通块(且\(a,b\)路径上的i+1个点属于不同联通块),这就可以使用凯利公式\(T_{n,i+1}=(i+1)*n^{n-i-2}\)

接下来就是计算\(i+1\)条边权乘积为m的方案数。如果是简化版题目中的和为\(m\),则只要使用隔板法就可以用一个组合数表示,然而乘积的情况下,就等对原题目进行质因数分解,转化为质因数的指数幂形式,在对每个质因数的指数分别进行隔板法再结果相乘(而且每个边代表的指数是可以为0的)。假设质因数p的指数幂为k,则隔板法方案数为\(C_{k+i-1}^{i-1}\)。

但是本题m非常大,常规质因数分解算法\(O(\sqrt{m})\)的质因数分解算法无法使用,这里想到的是使用大数因子分解算法Pollard Rho结合Miller Rabin提取素数因子。

如果提示中不给出凯利公式,那应该就是一道高难度的题了(给出了难度也不低就是)

代码

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<ctime>
#include<map>
using namespace std;
typedef long long ll;
const int MAXN = 1000005 ;
const int MOD=1e9+7;

///logn快速乘法
ll mulEx(ll a , ll b , ll Mod) {
    if(!a) return 0 ;
    ll ans(0) ;
    while(b)
    {
		if(b & 1) ans = (ans + a) % Mod;
		a <<= 1 ;
		a %= Mod ;
		b >>= 1 ;
    }
    return ans ;
}
 
///快速幂
ll powEx(ll base , ll n , ll Mod)
{
    ll ans(1) ;
    while(n)
    {
        if(n & 1) ans = mulEx(ans , base , Mod) ;
        base = mulEx(base , base , Mod) ;
        n >>= 1 ;
    }
    return ans ;
}
 
bool check(ll a , ll d , ll n)
{
    if(n == a) return true ;
    while(~d & 1) d >>= 1 ;
    ll t = powEx(a , d , n) ;
    while(d < n - 1 && t != 1 && t != n - 1)
    {
        t = mulEx(t , t , n) ;
        d <<= 1 ;
    }
    return (d & 1) || t == n - 1 ;
}

///判断大数是否是质数,miller_rabin
bool isP(ll n)
{ 
    if(n == 2) return true ;
    if(n < 2 || 0 == (n & 1)) return false ;
    static int p[5] = {2 , 3 , 7 , 61 , 24251} ;
    for(int i = 0 ; i < 5 ; ++ i) if(!check(p[i] , n - 1 , n)) return false ;
    return true ;
}
 
ll gcd(ll a , ll b)
{
    if(a < 0) return gcd(-a , b) ;
    return b ? gcd(b , a - b * (a / b)) : a ;
}
 
///大数分解质因数
ll Pollard_rho(ll n , ll c)
{
    ll i = 1 , k = 2 , x = rand() % n , y = x ;
    while(true)
    {
        x = (mulEx(x , x , n) + c) % n ;
        ll d = gcd(y - x , n) ;
        if(d != 1 && d != n) return d ;
        if(y == x) return n ;
        if(++ i == k)
        {
            y = x ;
            k <<= 1 ;
        }
    }
}
 
ll Fac[MAXN] , factCnt ;
///Fac存的是质因子,大小不一定按照顺序,有重复
map<ll , ll> factMap ;
///遍历map的first表示因子,second表示次数

void factorization(ll n)
{
    if(isP(n))
    {
        if (factMap.count(n)==0){
            Fac[factCnt++] = n ;
            factMap[n]=0;
        }
        return ;
    }
    ll p(n) ;
    while(p >= n) p = Pollard_rho(p , rand() % (n - 1) + 1) ;
    factorization(p) ;
    factorization(n / p) ;
}
 
 
void getFactor(ll x)
{
    if (isP(x)){
        Fac[factCnt++]=x;
        factMap[x]=0;
        return;
    }
	srand(time(0)) ;
	factCnt = 0 ;
	factMap.clear() ;
	factorization(x) ;
}
ll fac[2000005],ifac[2000005],nexp[1000005],mexp[1000005];
void init(ll n,ll m){
    ifac[1]=1;ifac[0]=1;
    fac[0]=1;fac[1]=1;
    for (ll i=2;i<=2*n;i++){
        fac[i]=fac[i-1]*i%MOD;
        ifac[i]=1ll*(MOD-MOD/i)*ifac[MOD%i]%MOD;
    }
    for (ll i=1;i<=2*n;i++){
        ifac[i]=ifac[i-1]*ifac[i]%MOD;
    }
    nexp[0]=1;nexp[1]=n;
    mexp[0]=1;mexp[1]=m%MOD;
    for (int i=2;i<=n;i++){
        nexp[i]=nexp[i-1]*n%MOD;
        mexp[i]=mexp[i-1]*(m%MOD)%MOD;
    }
}
//x>y
ll getC(int x,int y){
    return fac[x]*ifac[y]%MOD*ifac[x-y]%MOD;
}
int main(){
    int a,b;
    ll m,n;
    cin>>n>>m>>a>>b;
    init(n,m);
    ll invn=powEx(n,MOD-2,MOD);

    if (m==1){
        ll ans=0;
        for (int i=1;i<=n-1;i++){
            ll sums=1;
            sums=sums*fac[i-1]%MOD;
            sums=sums*getC(n-2,i-1)%MOD;
            if (n-i-2>=0)
                sums=sums*(i+1)%MOD*nexp[n-i-2]%MOD;
            else sums=sums*(i+1)%MOD*powEx(n,MOD-2,MOD)%MOD;
            ans=(ans+sums)%MOD;
        }
        cout<<ans;
        return 0;
    }
    getFactor(m);
    ll v=m;
    for (int i=0;i<factCnt;i++){
        while (v%Fac[i]==0){
            factMap[Fac[i]]++;
            v/=Fac[i];
        }
    }
    ll ans=0;
    for (int i=1;i<=n-1;i++){
        ll sums=1;
        for (int j=0;j<factCnt;j++){
            int tj=factMap[Fac[j]];
            sums=sums*getC(tj+i-1,i-1)%MOD;
        }
        sums=sums*fac[i-1]%MOD;
        sums=sums*mexp[n-1-i]%MOD;
        sums=sums*getC(n-2,i-1)%MOD;
        if (n-i-2>=0)
            sums=sums*(i+1)%MOD*nexp[n-i-2]%MOD;
        else sums=sums*(i+1)%MOD*invn;
        ans=(ans+sums)%MOD;
    }
    cout<<ans;
}```

标签:include,return,乘积,ll,路径,sums,ans,树上,MOD
来源: https://www.cnblogs.com/Y-E-T-I/p/16521183.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有