版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_33229466/article/details/82559329
题意
给出一个n个点m条边的带权无向图,问有多少棵生成树满足边权和模k等于0,答案模p输出。
。
分析
一个显然的做法就是把每条边的边权看成是
,然后做矩阵树定理,最后得出来多项式的
项系数的和就是答案。
但答案多项式的次数是
级别的,显然不能做。注意到如果我们把卷积变成模
意义下的循环卷积,那么最后常数项即为答案。
显然最后多项式的次数是
,我们可以考虑求出
个点值然后插值。
问题在于如何模拟循环卷积。如果我们像NTT那样,选择
个不同的满足
的
作为横坐标,然后带进去就可以得到循环卷积后的点值了。
注意到
是质数所以必然存在原根,又有
是
的倍数,所以
就可以取
,其中
。
时间复杂度
。
代码
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
typedef long long LL;
const int N=105;
int n,m,k,MOD,stack[25],xi[N],yi[N],a[N][N];
struct edge{int x,y,w;}e[N*N];
int read()
{
int x=0,f=1;char ch=getchar();
while (ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while (ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
return x*f;
}
int ksm(int x,int y)
{
int ans=1;
while (y)
{
if (y&1) ans=(LL)ans*x%MOD;
x=(LL)x*x%MOD;y>>=1;
}
return ans;
}
int get_g(int n)
{
int tmp=n-1,top=0;
for (int i=2;i*i<=tmp;i++)
if (tmp%i==0)
{
stack[++top]=i;
while (tmp%i==0) tmp/=i;
}
if (tmp>1) stack[++top]=tmp;
for (int i=2;;i++)
{
bool flag=0;
for (int j=1;j<=top;j++)
if (ksm(i,(n-1)/stack[j])==1) {flag=1;break;}
if (!flag) return i;
}
}
int gauss(int n)
{
int ans=1;
for (int i=1;i<=n;i++)
{
if (!a[i][i])
{
for (int j=i+1;j<=n;j++)
if (a[j][i])
{
for (int k=i;k<=n;k++) std::swap(a[j][k],a[i][k]);
ans=-ans;
break;
}
}
for (int j=i+1;j<=n;j++)
if (a[j][i])
{
int w=(LL)a[j][i]*ksm(a[i][i],MOD-2)%MOD;
for (int k=i;k<=n;k++) (a[j][k]+=MOD-(LL)a[i][k]*w%MOD)%=MOD;
}
ans=(LL)ans*a[i][i]%MOD;
}
return (ans+MOD)%MOD;
}
int main()
{
n=read();m=read();k=read();MOD=read();
int g=get_g(MOD);
for (int i=1;i<=m;i++) e[i].x=read(),e[i].y=read(),e[i].w=read();
for (int i=1;i<=k;i++)
{
xi[i]=ksm(g,(MOD-1)/k*(i-1));
memset(a,0,sizeof(a));
for (int j=1;j<=m;j++)
{
int u=e[j].x,v=e[j].y,w=e[j].w,t=ksm(xi[i],w);
(a[u][u]+=t)%=MOD;(a[v][v]+=t)%=MOD;
(a[u][v]+=MOD-t)%=MOD;(a[v][u]+=MOD-t)%=MOD;
}
yi[i]=gauss(n-1);
}
int ans=0;
for (int i=1;i<=k;i++)
{
int w=yi[i];
for (int j=1;j<=k;j++)
if (i!=j) w=(LL)w*(MOD-xi[j])%MOD*ksm(xi[i]+MOD-xi[j],MOD-2)%MOD;
(ans+=w)%=MOD;
}
printf("%d\n",ans);
return 0;
}