题目大意:
给一个
N
N
N 个点
M
M
M 条边的无向图
问有多少个无序三元组
(
C
,
x
,
y
)
(C,x,y)
(C,x,y)
满足
C
C
C 中的元素在
x
→
y
x\rightarrow y
x→y 的所有简单路径中
其中
C
C
C 是一个集合
x
,
y
∈
C
x,y\in C
x,y∈C
n
≤
1
0
5
,
m
≤
5
∗
1
0
5
n\le 10^5,m\le5*10^5
n≤105,m≤5∗105
部分分有树,仙人掌,随机树等等
考虑树的部分分,点分治
N
T
T
NTT
NTT 求出所有长度为
k
k
k 的路径后可以把贡献的式子拆开后再用
N
T
T
NTT
NTT 求出所有答案
考虑一般图(仙人掌)
其实一版图和仙人掌处理方法差不多
显然路径经过一个点双那么点双内的点必然被全部经过
除了到一个点双的端点的时候
直接上广义圆方树就行了,方点的权值为点双内的点数
−
2
-2
−2,圆点的权值为
1
1
1
把圆点直接算贡献即可
这时候点分治
N
T
T
NTT
NTT 会多一些细节,注意一下细节就好了
#include<bits/stdc++.h>
using namespace std;
#define rep(i,j,k) for(int i = j;i <= k;++i)
#define repp(i,j,k) for(int i = j;i >= k;--i)
#define ll long long
#define file(x) memset(x,0,sizeof(x))
#define pb push_back
#define SZ(x) ((int)(x.size()))
namespace io {
const int SIZE = (1 << 21) + 1;
char ibuf[SIZE], *iS, *iT, obuf[SIZE], *oS = obuf, *oT = oS + SIZE - 1, c, qu[55]; int f, qr;
// getchar
#define gc() (iS == iT ? (iT = (iS = ibuf) + fread (ibuf, 1, SIZE, stdin), (iS == iT ? EOF : *iS ++)) : *iS ++)
// print the remaining part
inline void flush () {
fwrite (obuf, 1, oS - obuf, stdout);
oS = obuf;
}
// putchar
inline void putc (char x) {
*oS ++ = x;
if (oS == oT) flush ();
}
// input a signed integer
template <class I>
inline void gi (I &x) {
for (f = 1, c = gc(); c < '0' || c > '9'; c = gc()) if (c == '-') f = -1;
for (x = 0; c <= '9' && c >= '0'; c = gc()) x = x * 10 + (c & 15); x *= f;
}
// print a signed integer
template <class I>
inline void print (I &x) {
if (!x) putc ('0'); if (x < 0) putc ('-'), x = -x;
while (x) qu[++ qr] = x % 10 + '0', x /= 10;
while (qr) putc (qu[qr --]);
}
//no need to call flush at the end manually!
struct Flusher_ {~Flusher_(){flush();}}io_flusher_;
}
using io :: gi;
using io :: putc;
using io :: print;
const int p = 998244353;
inline int calc(int a,int b){return (a+b)%p;}
inline int del(int a,int b){return (a-b+p)%p;}
inline int mul(int a,int b){return 1ll*a*b%p;}
inline int max(int a,int b){return a>b?a:b;}
inline int ksm(int a,int x){int now = 1;for(;x;x>>=1,a=1ll*a*a%p) if(x&1) now = 1ll*now*a%p; return now;}
const int inv2 = ksm(2,p-2);
int n,m,tp;
int linkk[101000],t;
struct node{int n,y;}e[1001000];
int A[801000],B[801000],v[201000];
int dfn[101000],low[101000],tot,id;
vector<int>in[101000],G[201000];
stack<int>st;
namespace NTT{
const int g = 3;
int r[1601000],w[1601000];
void ntt(int *a,int f,int flen){
w[0] = 1; rep(i,0,flen-1) r[i] = (r[i>>1]>>1) | ((i&1)?flen/2:0);
rep(i,0,flen-1) if(i < r[i]) swap(a[i],a[r[i]]);
for(int len = 2;len <= flen;len <<= 1){
int wn = ksm(g,(p-1)/len); if(f == -1) wn = ksm(wn,p-2);
rep(i,1,len-1) w[i] = mul(w[i-1],wn);
for(int st = 0;st < flen;st += len)
rep(i,0,(len>>1)-1){
int x = a[st+i],y = mul(a[st+(len>>1)+i],w[i]);
a[st+i] = calc(x,y); a[st+(len>>1)+i] = del(x,y);
}
}
if(f == -1){int inv = ksm(flen,p-2);rep(i,0,flen-1) a[i] = mul(a[i],inv);}
}
void Mul(int *a,int n,int *b,int m){
int flen = 1; while(flen < n+m-1) flen <<= 1;
rep(i,n,flen-1) a[i] = 0; rep(i,m,flen-1) b[i] = 0;
ntt(a,1,flen); ntt(b,1,flen); rep(i,0,flen-1) a[i] = mul(a[i],b[i]);
ntt(a,-1,flen);
}
void Mul2(int *a,int n){
int flen = 1; while(flen < 2*n-1) flen <<= 1;
rep(i,n,flen-1) a[i] = 0; ntt(a,1,flen); rep(i,0,flen-1) a[i] = mul(a[i],a[i]); ntt(a,-1,flen);
}
}using namespace NTT;
namespace work{
int f[201000],sz[201000],rt,now_size;
int ans[201000],a[801000],b[801000],mx,tot_mx;
bool inq[201000];
void get_rt(int x,int fa){
f[x] = 0; sz[x] = v[x];
rep(i,0,SZ(G[x])-1) if(!inq[G[x][i]] && G[x][i] != fa){
get_rt(G[x][i],x); f[x] = max(f[x],sz[G[x][i]]); sz[x] += sz[G[x][i]];
}
f[x] = max(f[x],now_size-sz[x]); if(f[x] < f[rt]) rt = x;
}
void get_dis(int x,int fa,int dep){
if(x <= n) a[dep]++; mx=max(mx,dep);
rep(i,0,SZ(G[x])-1) if(!inq[G[x][i]] && G[x][i] != fa) get_dis(G[x][i],x,dep+v[G[x][i]]);
}
void tr_calc(int x,int gg,int f){
mx = 0;get_dis(x,0,0); f += (gg==1?1:2)*(x<=n?1:v[x]);
NTT::Mul2(a,mx+1);
rep(i,0,2*mx) ans[i+f-1] = calc(ans[i+f-1],mul(gg,a[i]));
rep(i,0,2*mx) a[i] = 0; tot_mx = max(tot_mx,2*mx+f-1);
}
void solve(int x){
inq[x] = true; tr_calc(rt,1,0);
rep(i,0,SZ(G[x])-1) if(!inq[G[x][i]])
tr_calc(G[x][i],-1,(x<=n?1:v[x])) , now_size = sz[G[x][i]] , rt = 0 ,
get_rt(G[x][i],x) , solve(rt);
}
void doit(){
rt = 0;f[0] = 2*now_size;
get_rt(1,0);solve(rt);
}
}using namespace work;
namespace final{
int fac[301000],inv[301000];
inline int C(int x,int y){return (y<0||y>x)?0:mul(fac[x],mul(inv[y],inv[x-y]));}
void pre(){
fac[0] = inv[0] = 1;
rep(i,1,300000) fac[i] = 1ll*fac[i-1]*i%p,inv[i] = ksm(fac[i],p-2);
}
void solve1(){
rep(i,1,work::tot_mx) work::ans[i] = mul(work::ans[i],inv2);
//ans[i]->i+1¸ö
rep(i,0,n-2) A[i] = mul(work::ans[n-i-1],fac[n-i-2]);
rep(i,0,n-1) B[i] = inv[i];
Mul(A,n,B,n);
rep(i,2,n) printf("%d\n",mul(A[n-i],inv[i-2]));
}
}
void insert(int x,int y){
e[++t].y = y;e[t].n = linkk[x];linkk[x] = t;
e[++t].y = x;e[t].n = linkk[y];linkk[y] = t;
}
void tarjan(int x,int fa){
dfn[x] = low[x] = ++tot; st.push(x);
for(int i = linkk[x];i;i = e[i].n) if(i != fa) {
int y = e[i].y;
if(!dfn[y]) {
tarjan( y , i );
low[x] = min(low[x],low[y]);
if(low[y] >= dfn[x]) {
id++;
while(st.top() != y) G[id].pb(st.top()),G[st.top()].pb(id),st.pop(),v[id]++;
G[id].pb(st.top()); G[st.top()].pb(id); st.pop(); G[x].pb(id); G[id].pb(x);
work::now_size += v[id];
}
}else low[x] = min(low[x],dfn[y]);
}
}
int main(){
freopen("railway.in","r",stdin);
freopen("railway.out","w",stdout);
gi(n); gi(m);int x,y;
rep(i,1,m) gi(x),gi(y),insert(x,y);
rep(i,1,n) v[i] = 1; id = n; work::now_size = n;
tarjan(1,0);
work::doit(); final::pre();
final::solve1();
return 0;
}