- 題解:首先考慮一棵樹怎麼做,就是樹形 dp,然後我們可以枚舉每一條邊怎麼選,顯然有 3 種情況
進一步發現只需要枚舉兩種,即強制 u 選 v 不選,和強制 u 不選 v 隨意
那麼現在的問題就是每次 ban 掉一些點選一些點不選,動態更新根節點的 dp 值
然後把轉移寫成矩陣的形式鏈分治,由於可以除 0 所以手寫了一個用 x∗0y 表示每個數的類
複雜度 O(n+k∗2k∗log(n)2) 被虛樹吊打
using namespace std;
int read(){
int cnt = 0, f = 1; char ch = 0;
while(!isdigit(ch)){ ch = getchar(); if(ch == '-') f = -1; }
while(isdigit(ch)) cnt = cnt*10 + (ch-'0'), ch = getchar();
return cnt * f;
}
cs int N = 1e5 + 50, K = 15;
cs int Mod = 998244353;
int add(int a, int b){ return a + b >= Mod ? a + b - Mod : a + b; }
int mul(int a, int b){ return 1ll * a * b % Mod; }
int dec(int a, int b){ return a - b < 0 ? a - b + Mod : a - b; }
int ksm(int a, int b){ int ans=1; for(;b;b>>=1,a=mul(a,a)) if(b&1) ans=mul(ans,a); return ans; }
void Add(int &a, int b){ a = add(a, b); }
void Mul(int &a, int b){ a = mul(a, b); }
int n, m, k, u[K], v[K];
vector<int> G[N];
int sz[N], son[N], fa[N], dep[N], in[N], top[N], bot[N], sgn;
struct mat{
int a[2][2];
mat(int _x=0, int _y=0){ a[0][0]=a[1][0]=_x; a[0][1]=_y; a[1][1]=0; }
mat operator * (cs mat &A){
mat B;
for(int i=0; i<2; i++) for(int k=0; k<2; k++) if(a[i][k])
for(int j=0; j<2; j++) if(A.a[k][j]) Add(B.a[i][j],mul(a[i][k],A.a[k][j]));
return B;
}
int vl(){ return add(a[0][0],a[0][1]); }
};
struct num{
int x,y;
num(){ x = y = 1; }
void operator = (int v){ v ? (x=v,y=0) : (x=y=1); }
void operator *= (int v){ v ? x=mul(x,v) : ++y; }
void operator /= (int v){ v ? x=mul(x,ksm(v,Mod-2)) : --y; }
operator int()cs{ return y?0:x; }
};
void pre_dfs(int u, int f){
sz[u] = 1;
for(int v : G[u]) if(v ^ f){
fa[v] = u; dep[v] = dep[u]+1; pre_dfs(v,u);
sz[u] += sz[v]; if(sz[son[u]]<sz[v]) son[u]=v;
}
}
namespace SGT{
cs int N = ::N<<2;
mat vl[N];
void pushup(int x){ vl[x] = vl[x<<1|1] * vl[x<<1]; }
void modify(int x, int l, int r, int p, mat v){
if(l == r){ vl[x] = v; return; }
(p<=mid)?modify(x<<1,l,mid,p,v):modify(x<<1|1,mid+1,r,p,v); pushup(x);
}
mat query(int x, int l, int r, int L, int R){
if(L<=l&&r<=R) return vl[x];
if(R<=mid) return query(x<<1,l,mid,L,R);
else if(L>mid) return query(x<<1|1,mid+1,r,L,R);
else return query(x<<1|1,mid+1,r,L,R)*query(x<<1,l,mid,L,R);
}
}
int f[N][2]; num coe[N][2];
void dfs(int u, int tp){
top[u] = tp; in[u]=++sgn;
if(son[u]) dfs(son[u],tp), bot[u]=bot[son[u]];
else bot[u]=u;
coe[u][0]=1; coe[u][1]=1;
for(int v : G[u]) if(v!=fa[u]&&v!=son[u])
dfs(v,v), coe[u][0]*=add(f[v][0],f[v][1]), coe[u][1]*=f[v][0];
SGT::modify(1,1,n,in[u],mat(coe[u][0],coe[u][1]));
if(son[u]) f[u][0]=mul(coe[u][0],add(f[son[u]][0],f[son[u]][1])), f[u][1]=mul(coe[u][1],f[son[u]][0]);
else f[u][0]=f[u][1]=1;
}
int state[N];
void upt(int u){
mat now;
while(u){
int tp = fa[top[u]];
if(tp){
now = SGT::query(1,1,n,in[top[u]],in[bot[u]]);
coe[tp][0]/=now.vl(), coe[tp][1]/=now.a[0][0];
}
if(state[u]==1) SGT::modify(1,1,n,in[u],mat(coe[u][0],0));
else if(state[u]==2) SGT::modify(1,1,n,in[u],mat(0,coe[u][1]));
else SGT::modify(1,1,n,in[u],mat(coe[u][0],coe[u][1]));
if(tp){
now = SGT::query(1,1,n,in[top[u]],in[bot[u]]);
coe[tp][0]*=now.vl(), coe[tp][1]*=now.a[0][0];
} u = tp;
}
}
int anc[N];
int find(int x){ return x==anc[x]?x:anc[x]=find(anc[x]); }
bool ck(int S){
for(int i=0; i<k; i++) state[u[i]]=state[v[i]]=0;
for(int i=0; i<k; i++){
if(S>>i&1){
if(state[u[i]]==1) return false; state[u[i]]=2;
if(state[v[i]]==2) return false; state[v[i]]=1;
}
else{
if(state[u[i]]==2) return false; state[u[i]]=1;
}
} return true;
}
int main(){
n = read(), m = read();
for(int i=1; i<=n; i++) anc[i]=i;
for(int i=1,x,y; i<=m; i++){
x=read(), y=read();
if(find(x)^find(y)) G[x].pb(y),G[y].pb(x),anc[find(x)]=find(y);
else u[k]=x,v[k++]=y;
}
pre_dfs(1,0); dfs(1,1); int as = 0;
for(int S=0; S<(1<<k); S++){
if(!ck(S)) continue;
for(int i=0; i<k; i++){
if(S>>i&1) upt(u[i]), upt(v[i]);
else upt(u[i]);
}
Add(as,SGT::query(1,1,n,in[1],in[bot[1]]).vl());
for(int i=0; i<k; i++) state[u[i]]=state[v[i]]=0;
for(int i=k-1; ~i; i--){
if(S>>i&1) upt(v[i]), upt(u[i]);
else upt(u[i]);
}
} cout << as; return 0;
}