P10219 [省选联考 2024] 虫洞
一种不需要高级线性代数知识的低复杂度做法。
我们只考虑
依次加入每种颜色的边。加入第一种颜色时,我们得到了若干个环。
令点
进一步利用条件四,我们可以刻画第二种颜色的边:将环划分成若干个集合,要求每个集合中所有环大小相等。对于一个集合,令其中的环为
相当于将一个集合中的环合并成了一个“柱”。
再加入第三种颜色。通过一些分析可以得到:将“柱”划分为若干个集合,要求每个集合中所有“柱”同构。连边方式也与之前类似,即为每个“柱”向下一个“柱”的某一种同构方式的对应点连边。
以此类推,我们可以猜测出
实际上,我们可以通过归纳得到一个较为关键的性质:对于一个“连通块”,若看作无标号,那么其中任意两点都是等价的,即存在一种自同构使得这两个点对应,并且这种自同构是唯一的。
因此我们令
其中要求
其意义为这一步合并了
我们只需要对于每个
但
将
进一步将
只需维护
更进一步地,注意到分母形式的特殊性,我们可以将
按照上述转移式直接暴力维护
这个转移还可以通过类似于“在线高维前缀和”的方式进一步优化,令
参考代码(
#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define pb push_back
const int N=2e6+5,MOD=998244353;
int n,pw[N],id[N],z[N],z1[N];ll m;vector<int> d[N],f[N];
int l,lim,lim1,invL,r[N],inv[N],tmp1[N],tmp2[N],tmp3[N],g[2][N];
void W(int &x,int y) {x+=y;if(x>=MOD) x-=MOD;}
void W(int &x,ll y) {x=(x+y)%MOD;}
int add(int x,int y) {x+=y;return x<MOD?x:x-MOD;}
int qPow(int x,int y)
{int res=1;for(;y;y>>=1,x=1ll*x*x%MOD) if(y&1) res=1ll*res*x%MOD;return res;}
void init(int n)
{
l=0;lim=1;while(lim<n) ++l,lim*=2;invL=qPow(lim,MOD-2);
for(int i=0;i<lim;++i) r[i]=(r[i>>1]>>1)|((i&1)<<l-1);
if(lim>lim1)
{
for(int i=1;i<lim;++i) inv[i]=i>1?1ll*inv[MOD%i]*(MOD-MOD/i)%MOD:1;
for(int i=1,t1,t2,t3,t4;i<lim;i*=2)
{
t1=qPow(3,(MOD-1)/(i*2));t2=qPow(t1,MOD-2);t3=t4=1;
for(int j=0;j<i;++j,t3=1ll*t3*t1%MOD,t4=1ll*t4*t2%MOD)
g[0][i+j]=t3,g[1][i+j]=t4;
}lim1=lim;
}
}
void deriv(int n,int a[]) {for(int i=1;i<n;++i) a[i-1]=1ll*a[i]*i%MOD;a[n-1]=0;}
void integ(int n,int a[]) {for(int i=n-1;i;--i) a[i]=1ll*a[i-1]*inv[i]%MOD;a[0]=0;}
void NTT(bool fl,int a[])
{
for(int i=0;i<lim;++i) if(i<r[i]) swap(a[i],a[r[i]]);
for(int i=1,t1,t2;i<lim;i*=2) for(int j=0;j<lim;j+=i*2) for(int k=0;k<i;++k)
{
t1=a[j+k];t2=1ll*g[fl][i+k]*a[i+j+k]%MOD;
a[j+k]=add(t1,t2);a[i+j+k]=add(t1,MOD-t2);
}if(fl) for(int i=0;i<lim;++i) a[i]=1ll*a[i]*invL%MOD;
}
void polyInv(int n,int a[],int res[])
{
if(n==1) {res[0]=qPow(a[0],MOD-2);return;}polyInv((n+1)/2,a,res);
for(int i=0;i<n;++i) tmp1[i]=a[i];for(int i=n;i<lim;++i) tmp1[i]=0;
init(n*2-1);NTT(0,tmp1);NTT(0,res);
for(int i=0;i<lim;++i) res[i]=(2-1ll*tmp1[i]*res[i]%MOD+MOD)*res[i]%MOD;
NTT(1,res);for(int i=n;i<lim;++i) res[i]=0;
}
void polyLn(int n,int a[])
{
init(n*2-1);for(int i=0;i<lim;++i) tmp2[i]=0;
polyInv(n,a,tmp2);deriv(n,a);NTT(0,a);NTT(0,tmp2);
for(int i=0;i<lim;++i) a[i]=1ll*a[i]*tmp2[i]%MOD;
NTT(1,a);integ(n,a);for(int i=n;i<lim;++i) a[i]=0;
}
void polyExp(int n,int a[],int res[])
{
if(n==1) {res[0]=1;return;}polyExp((n+1)/2,a,res);
for(int i=0;i<n;++i) tmp3[i]=res[i];for(int i=n;i<lim;++i) tmp3[i]=0;
polyLn(n,tmp3);for(int i=0;i<n;++i) tmp3[i]=add(a[i],MOD-tmp3[i]);++tmp3[0];
NTT(0,tmp3);NTT(0,res);for(int i=0;i<lim;++i) res[i]=1ll*res[i]*tmp3[i]%MOD;
NTT(1,res);for(int i=n;i<lim;++i) res[i]=0;
}
int main()
{
scanf("%*d %d %*d %lld",&n,&m);m%=MOD-1;
for(int i=1;i<=n;++i)
{
pw[i]=qPow(i,m);inv[i]=i>1?1ll*inv[MOD%i]*(MOD-MOD/i)%MOD:1;
for(int j=i;j<=n;j+=i) d[j].pb(i);
}
for(int i=1,t;i<=n;++i)
{
f[i].resize(d[i].size());if(i==1) {f[i][0]=z[1]=1;continue;}
for(int j=0;j<d[i].size();++j) id[d[i][j]]=j;
for(auto j:d[i]) if(j<i) for(int k=0;k<d[j].size();++k)
W(f[i][id[d[j][k]]],f[j][k]);
for(int j=0;j<d[i].size();++j)
{
if(d[i][j]<i)
{
t=1ll*f[i][j]*(MOD-inv[i-d[i][j]])%MOD;
f[i][j]=t;W(f[i].back(),MOD-t);
}W(z[i],1ll*f[i][j]*pw[d[i][j]]);
}z[i]=1ll*z[i]*inv[i]%MOD;for(auto &x:f[i]) x=1ll*x*i%MOD;
}polyExp(n+1,z,z1);for(int i=1;i<=n;++i) z1[n]=1ll*z1[n]*i%MOD;
printf("%d\n",z1[n]);return 0;
}