题解:AT_abc387_g [ABC387G] Prime Circuit
问题大意
求有
对于
环可以重复经过同一顶点,但不能重复经过同一条边。
解法
借鉴了 at 第二篇题解。
首先我们发现如果图
因为这种环绝对是由两个及以上的简单环(即没有重复经过同一顶点)拼起来的。
而如果这个环由两个简单环拼凑起来,由于原图是简单无向图,于是没有长度为
有由两个以上的简单环拼起来的环的图绝对有由两个简单环拼起来的环,于是也不合法。
所以我们知道绝对不会有两个环有点相交。
于是如果我们将原图的环全部缩成点,那么原图就会变成一棵树。
根据扩展卡莱定理,有
想看证明可以看这篇博客,主要因为我不会证。
那么再将式子化简一下,就可以得到
那么,由于树上节点可以是环缩成的也可以本来就是一个点,于是环长
那么答案就是先枚举缩完点树上有
其中
乘上
由于
由于点有标号,所以是排列,所以考虑对于
对于总答案
多项式
答案就是生成函数的第
别忘了最后再乘上
code
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=1e6+10;
const int p=998244353,y=3,ny=332748118;
int n;
int r[N],a[N],ans[N],f[N],g[N];
int ksm(int a,int b=p-2){
int ans=1;
for(;b;b>>=1,a=a*a%p) if(b&1) ans=ans*a%p;
return ans;
}
void NTT(int *a,int len,int type){
for(int i=1;i<len;i++) if(i<r[i]) swap(a[i],a[r[i]]);
for(int mid=1;mid<len;mid<<=1){
const int wn=ksm((type?y:ny),(p-1)/(mid<<1));
for(int R=mid<<1,j=0;j<len;j+=R){
int w(1);
for(int k=0;k<mid;k++,w=w*wn%p){
int x=a[j+k],y=a[j+k+mid]*w%p;
a[j+k]=(x+y)%p;
a[j+k+mid]=(x+p-y)%p;
}
}
}
if(type) return;
int inv=ksm(len);
for(int i=0;i<len;i++) a[i]=a[i]*inv%p;
}
void inv(int *a,int *b,int len){
if(len==1) {b[0]=ksm(a[0]);return;}
inv(a,b,(len+1)>>1);
int m=len<<1;
int limit,l;
for(limit=1,l=0;limit<m;limit<<=1,l++);
for(int i=1;i<limit;i++) r[i]=(r[i>>1]>>1)|((i&1)<<(l-1));
for(int i=0;i<len;i++) f[i]=a[i];
for(int i=len;i<limit;i++) f[i]=0;
NTT(f,limit,1),NTT(b,limit,1);
for(int i=0;i<limit;i++) b[i]=b[i]*((2+p-f[i]*b[i]%p)%p)%p;
NTT(b,limit,0);
for(int i=len;i<limit;i++) b[i]=0;
}
void mul(int*f,int *g,int l1,int l2){
int limit,l;
for(limit=1,l=0;limit<=l1+l2;limit<<=1,l++);
for(int i=1;i<limit;i++) r[i]=(r[i>>1]>>1)|((i&1)<<(l-1));
NTT(f,limit,1),NTT(g,limit,1);
for(int i=0;i<limit;i++) f[i]=f[i]*g[i]%p;
NTT(f,limit,0);
for(int i=limit-1;i>=1;i--) f[i]=f[i-1]*ksm(i,p-2)%p;
}
int h[N];
void ln(int *a,int *b,int len){
for(int i=1;i<len;i++) h[i-1]=a[i]*i%p;
inv(a,b,len);
mul(b,h,len,len-1);
b[0]=0;
}
void expp(int *a,int *b,int len){
if(len==1) {b[0]=1;return;}
expp(a,b,(len+1)>>1);
int m=len<<1,limit,l;
for(limit=1,l=0;limit<m;limit<<=1,l++);
for(int i=0;i<limit;i++) g[i]=0;
ln(b,g,len);
for(int i=len;i<limit;i++) g[i]=0;
for(int i=0;i<len;i++) f[i]=a[i];
for(int i=len;i<limit;i++) f[i]=0;
for(int i=1;i<limit;i++) r[i]=(r[i>>1]>>1)|((i&1)<<(l-1));
NTT(f,limit,1),NTT(g,limit,1),NTT(b,limit,1);
for(int i=0;i<limit;i++) b[i]=((1-g[i]+p)%p+f[i])%p*b[i]%p;
NTT(b,limit,0);
for(int i=len;i<limit;i++) b[i]=0;
}
int pr[N],vis[N],cnt;
signed main(){
cin>>n;
for(int i=2;i<=3e5;i++){//筛素数
if(!vis[i]) pr[++cnt]=i;
for(int j=1;j<=cnt&&pr[j]*i<=3e5;j++){
vis[i*pr[j]]=1;
if(!(i%pr[j])) break;
}
}
a[1]=n;
for(int i=2;i<=cnt;i++) a[pr[i]]=n*((p+1)/2)%p;//F(x)函数
expp(a,ans,n+10);//多项式exp
int an=ans[n];//得到第n项
for(int i=1;i<=n;i++) an=an*i%p;
cout<<an*ksm(n*n%p,p-2)%p;
return 0;
}