题目大意:
n
n
n 个点的树(基环树)
每次随机选取一个点进行操作,删去该点并将答案加上这个点属于的联通块的大小
问答案的期望
n
≤
1
0
5
,
n
−
1
≤
m
≤
n
n\le 10^5,n-1\le m\le n
n≤105,n−1≤m≤n
先考虑树怎么做,显然删去点 x x x 时 y y y 与 x x x 联通的概率为 1 d i s ( x , y ) \frac{1}{dis(x,y)} dis(x,y)1 ,其中, d i s ( x , y ) dis(x,y) dis(x,y)表示 x x x 到 y y y 的路径上的点的个数
显然可以点分治 + N T T +NTT +NTT搞
考虑基环树,如果路径不经过环显然不影响。
考虑经过环的路径,设
(
a
→
b
)
(a\rightarrow b)
(a→b)的路径交环于
c
,
d
c,d
c,d
令
z
=
d
i
s
(
a
,
c
)
+
d
i
s
(
b
,
d
)
z=dis(a,c)+dis(b,d)
z=dis(a,c)+dis(b,d) ,
x
,
y
x,y
x,y 分别为
c
,
d
c,d
c,d 之间的两条路径的长度
那么容斥一下就可以得到概率
1
x
+
z
+
1
y
+
z
−
1
x
+
y
+
z
\frac{1}{x+z}+\frac{1}{y+z}-\frac{1}{x+y+z}
x+z1+y+z1−x+y+z1
可以发现
x
+
z
x+z
x+z 和
y
+
z
y+z
y+z 分别为两个不同的
d
i
s
(
a
,
b
)
dis(a,b)
dis(a,b)
考虑删去一掉边再点分治
+
N
T
T
+NTT
+NTT,漏掉的就是过环的另一个
d
i
s
(
a
,
b
)
dis(a,b)
dis(a,b) 和第三项
漏掉的我们可以把环上所有子树根据距删去的边进行差分后直接 N T T NTT NTT,此时子树内会算重,我们再对子树点分治 + N T T +NTT +NTT
第三项只要把所有子树的信息合起来做一次 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;}
int n,m,u,v,tp;
int linkk[101000],t,du[101000],son[101000];
struct node{int n,y;}e[201000];
int inv[1001000],g[101000];
int tmp[101000],tot,dep[101000];
int A[801000],B[801000];
int mx_dep[101000];
vector<int>in[101000];
namespace NTT{
const int G = 3;
int r[801000],w[801000];
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 cir{
queue<int>q;
bool vis[101000];
void find_circle(){
rep(i,1,n) if(du[i] == 1) vis[i] = true,q.push(i);
while(!q.empty()){
int x = q.front();q.pop();
for(int i = linkk[x];i;i = e[i].n) if(!vis[e[i].y]){ du[e[i].y]--;if(du[e[i].y] == 1) q.push(e[i].y),vis[e[i].y] = true; }
}
rep(i,1,n) if(!vis[i]) {tmp[++tot] = i;break;}
int x = tmp[tot]; for(int i = linkk[x];i;i = e[i].n) if(!vis[e[i].y]) {x = e[i].y;break;}
while(x != tmp[1]) {tmp[++tot] = x;for(int i = linkk[x];i;i = e[i].n) if(!vis[e[i].y] && e[i].y != tmp[tot-1]){x = e[i].y;break;}}
u = tmp[1];v = tmp[tot];
}
}using namespace cir;
namespace work{
int f[101000],sz[101000],rt,now_size;
int ans[101000],a[401000],b[401000],mx,tot_mx;
bool inq[101000],ok;
void get_rt(int x,int fa){
f[x] = 0;sz[x] = 1;
for(int i = linkk[x];i;i = e[i].n) if(!inq[e[i].y] && e[i].y != fa) get_rt(e[i].y,x),f[x] = max(f[x],sz[e[i].y]),sz[x] += sz[e[i].y];
f[x] = max(f[x],now_size-sz[x]); if(f[x] < f[rt]) rt = x;
}
void get_dis(int x,int fa,int v){a[v]++; mx=max(mx,v); for(int i = linkk[x];i;i = e[i].n) if(!inq[e[i].y] && e[i].y != fa) get_dis(e[i].y,x,v+1);}
void tr_calc(int x,int v,int f){
mx = 0;get_dis(x,0,f);
NTT::Mul2(a,mx+1);
rep(i,0,2*mx) ans[i] = calc(ans[i],mul(v,a[i]));
rep(i,0,2*mx) a[i] = 0; tot_mx = max(tot_mx,2*mx);
}
void solve(int x){
inq[x] = true; tr_calc(rt,1,0);
for(int i = linkk[x];i;i = e[i].n) if(!inq[e[i].y]) tr_calc(e[i].y,-1,1) , now_size = sz[e[i].y] , rt = 0 , get_rt(e[i].y,x) , solve(rt);
}
void doit(int x,int sz){
if(x == 1 && sz == 2) ok = true;
rt = 0;now_size = sz;f[0] = 2*n;
get_rt(x,0);solve(rt);
if(x == 1 && sz == 2) ok = false;
}
void again(){
rep(i,0,tot_mx) ans[i] = 0;
tot_mx = 0;
}
}using namespace work;
void insert(int x,int y){
e[++t].y = y;e[t].n = linkk[x];linkk[x] = t;du[x]++;
e[++t].y = x;e[t].n = linkk[y];linkk[y] = t;du[y]++;
}
void dele(int x,int y){if(e[linkk[x]].y == y) {linkk[x] = e[linkk[x]].n;return;}for(int i = linkk[x];i;i = e[i].n) if(e[e[i].n].y == y) {e[i].n = e[e[i].n].n;return;}}
void get_sz(int x,int fa){
dep[x] = dep[fa] + 1; if(dep[x] > mx_dep[tp]) mx_dep[tp]++,in[tp].pb(0); in[tp][dep[x]]++; sz[x] = 1;
for(int i = linkk[x];i;i = e[i].n) if(!inq[e[i].y] && e[i].y != fa) get_sz(e[i].y,x),sz[x] += sz[e[i].y];
}
int fuck;
int f1(){
int now = 0,n = 0,m = 0;
rep(i,1,tot) {rep(j,0,SZ(in[i])-1) A[j+i-1] = calc(A[i+j-1],in[i][j]);n = max(n,i+SZ(in[i])-1);}
rep(i,1,tot) {rep(j,0,SZ(in[i])-1) B[j+tot-i] = calc(B[j+tot-i],in[i][j]);m = max(m,tot-i+SZ(in[i]));}
NTT::Mul(A,n,B,m);
rep(i,0,n+m-2) now = calc(now,mul(inv[i+2],A[i])),A[i] = 0;
return now;
}
int f2(){
int now = 0,n = 0;
rep(i,1,tot){
rep(j,0,SZ(in[i])-1) A[j] = calc(A[j],in[i][j]);
n = SZ(in[i]); Mul2(A,n);
rep(i,0,2*n-2) {
now = del(now,mul(inv[2],mul(inv[i+tot],A[i])));
now = calc(now,mul(inv[i+1+tot],A[i])),A[i] = 0;
}
}
now = del(now,mul(fuck,inv[2]));
return now;
}
int f3(){
int now = 0,n = 0;
rep(i,1,tot) rep(j,0,SZ(in[i])-1) A[j] = calc(A[j],in[i][j]),n = max(n,SZ(in[i]));
Mul2(A,n); rep(i,0,2*n-2) now = calc(now,mul(inv[i+tot],A[i]));
return now;
}
void Solve(){
int ans = 0,now = 0;
rep(i,1,200000) inv[i] = ksm(i,p-2);
rep(i,0,work::tot_mx) ans = calc(ans,mul(inv[i+1],work::ans[i])),now = calc(now,mul(inv[i+1+tot],work::ans[i]));
if(m == n-1){printf("%d\n",ans);exit(0);}
file(work::inq); rep(i,1,tot) work::inq[tmp[i]] = true;dep[0] = -1;
rep(i,1,tot){
work::inq[tmp[i]] = false;mx_dep[i] = -1; tp = i; get_sz(tmp[i],0); work::again(); work::doit(tmp[i],sz[tmp[i]]);
rep(i,0,work::tot_mx) now = del(now,mul(work::ans[i],inv[i+1+tot]));
}
now = mul(now,inv[2]);now = del(f1(),now);now = del(now,f2());
now = mul(now,2);ans = calc(ans,now);ans = del(ans,f3());
printf("%d\n",ans);
}
int main(){
freopen("eat.in","r",stdin);
freopen("eat.out","w",stdout);
gi(n);gi(m);int x,y;
rep(i,1,m) gi(x),gi(y),insert(x,y); if(m == n-1) goto loop;
cir::find_circle(); dele(tmp[1],tmp[tot]); dele(tmp[tot],tmp[1]);
loop:; work::doit(1,n);
Solve();
return 0;
}