P9357 「SiR-1」Lighthouse 题解

首先我们设 

(

,

)
F(u,v) 表示 

u 和 

v 连通的时候,

u 往上蹦一下的情况数量。

显而易见,答案是 


=
1



=
1


(

,

)
∑ 
i=1
n

 ∑ 
j=1
n

 F(i,j)。

然后你发现,如果一条路长度相同,那么他并没有什么不一样的,因为道路内的没有实际差异,道路外的我不在乎,也没有实际差异。

所以你决定枚举长度 

l,假设 



l

  为所有长度为 

l 的路径的 

F,假设 




cnt 
l

  表示长度为 

l 的路径数量,那么现在答案是 


=
1







∑ 
l=1
n

 cnt 
l

 G 
l

 。





cnt 
l

  我们很方便计算,考虑 



l

  怎么计算。

先枚举 

w 表示第 

w 层的时候路径端点开始蹦。

然后你发现总共要在路径中做 


wl 次操作,每个点恰好做 

w 次。

这个不难,首先打标签全排列,然后再去标签。

然后假设是第 

k 次操作拿下的。

那就是,


1
k−1 次操作中安排 


wl 次操作,然后剩下 


1



k−1−wl 次操作,可以选择路径外的所有点。

剩下 



m−k 次操作,可以选择所有点因为目的已经达到了后面什么样子不在乎了。

式子就长这样:



=



<

(


)
!
(

!
)



=


+
1




1


(



)


1








l

 = 
wl

  
(w!) 
l
 
(wl)!

  
k=wl+1

m

 C 
k−1
wl

 (n−l) 
k−1−wl
 n 
m−k
 

然后你发现直接嗯算就是 

2
ln



2
 lnn 的,非常的愚蠢,我们需要来优化他。

首先就是那个低能组合数,我们必须把他拿下来,但是很明显拿不下来,那我们就考虑整 

k 次操作在路径上。



=



<

(


)
!
(

!
)



=


+
1




(

)


1



(



)




l

 = 
wl

  
(w!) 
l
 
(wl)!

  
k=wl+1

m

 C 
m
k

 (l) 
k−1−wl
 (n−l) 
m−k
 

这个式子非常好看,但是还不够,我们容不得 

w 在后面出现!

所以我们把他变成了这个样子。



=



<

(


)
!
(

!
)







=


+
1




(

)


1
(



)




l

 = 
wl

  
(w!) 
l
 
(wl)!

 l 
−wl
  
k=wl+1

m

 C 
m
k

 (l) 
k−1
 (n−l) 
m−k
 

很好,后面没有 

w 了,这意味着后面的式子只与 

k 和 

l 有关了。

但是还不够,写一下就会发现难写,所以我们再化一下。



=



<

(


)
!
(

!
)







=


+
1




(

)
(



)
+
(


1
)
(



)




l

 = 
wl

  
(w!) 
l
 
(wl)!

 l 
−wl
  
k=wl+1

m

 C 
m
k

 (l) 
(k−m)+(m−1)
 (n−l) 
m−k
 



=



<

(


)
!
(

!
)







=


+
1




(

)
(


1
)
(




)




l

 = 
wl

  
(w!) 
l
 
(wl)!

 l 
−wl
  
k=wl+1

m

 C 
m
k

 (l) 
(m−1)
 ( 
l
n−l

 ) 
m−k
 



=



<

(


)
!
(

!
)







1


=


+
1




(




)




l

 = 
wl

  
(w!) 
l
 
(wl)!

 l 
m−wl−1
  
k=wl+1

m

 C 
m
k

 ( 
l
n−l

 ) 
m−k
 

​那我们直接对于每一个 

l 预处理一遍这些式子,然后后缀和,就可以直接求了。

天才的想法!

时间复杂度 

(


+

ln


)
O(nm+mlnn)。

#include
#define int long long
using namespace std;
const int P=1000000007;
const int MAXN=200005;
int n,m;
vector ljb[1500];
int Cnt[1500];
int father[1500][11];
int dep[1500];
int Jc[MAXN];
int InvJc[MAXN];
int Pre[MAXN];
int Suf[MAXN];
int ans;
int Co[MAXN];
int power(int x,int y=P-2){
    if(y==0)return 1;
    int tmp=power(x,y>>1);
    if(y&1)return tmp*tmp%P*x%P;
    else return tmp*tmp%P;
}
void dfs(int cur,int fa){
    dep[cur]=dep[fa]+1;
    father[cur][0]=fa;
    for(int i=1;i<11;i++){
        father[cur][i]=father[father[cur][i-1]][i-1];
    }
    for(int i=0;i         int v=ljb[cur][i];
        if(v==fa)continue;
        dfs(v,cur);
    }
    return;
}
int LCA(int x,int y){
    if(dep[x]     for(int i=10;i>=0;i--){
        if(dep[father[x][i]]>=dep[y]){
            x=father[x][i];
        }
    }
    if(x==y)return x;
    for(int i=10;i>=0;i--){
        if(father[x][i]^father[y][i]){
            x=father[x][i];
            y=father[y][i];
        }
    }
    return father[x][0];
}
int dis(int x,int y){
    int lca=LCA(x,y);
    return dep[x]+dep[y]-dep[lca]-dep[father[lca][0]];
}
int C(int n,int m){
    return Jc[n]*InvJc[m]%P*InvJc[n-m]%P;
}
signed main(){
    scanf("%lld%lld",&n,&m);
    Jc[0]=1;
    InvJc[0]=1;
    for(int i=1;i<=200000;i++){
        Jc[i]=Jc[i-1]*i%P;
        InvJc[i]=power(Jc[i]);
    }
    for(int i=0;i<=m;i++){
        Co[i]=C(m,i);
    }
    for(int i=1;i         int u,v;
        scanf("%lld%lld",&u,&v);
        ljb[u].push_back(v);
        ljb[v].push_back(u);
    }
    dfs(1,0);
    for(int i=1;i<=n;i++){
        for(int j=1;j<=n;j++){
            int fw=dis(i,j);
            if(fw<1||fw>n){
                return j;
            }
            assert(fw>=1&&fw<=n);
            Cnt[fw]++;
        }
    }
    for(int l=1;l<=n;l++){
        int Fuck=(n-l)*power(l)%P;
        int D=1;
        for(int i=m;i>=0;i--){
            Suf[i]=(Suf[i+1]+C(m,i)*D%P)%P;
            D=D*Fuck%P;
        }
        int S=0;
        for(int w=0;w*l             int dinner=(Suf[w*l+1])*Jc[w*l]%P*(power(InvJc[w],l))%P*power(l,m-w*l-1)%P;
            S=(S+dinner)%P;
        }
        ans=(ans+S*Cnt[l]%P)%P;
    }
    printf("%lld",ans%P);
    return 0;
}

你可能感兴趣的:(算法,深度优先,数据结构)