P4426
SOL
虚树代码都敲完了,突然发现这是一道ddp板题。。。看来最近和板子挺有缘的。。
-
transfer the probelem : \text{transfer\ the\ probelem}: transfer the probelem:在有10条额外边的树上求点独立集个数。
-
基于原树思考。先求出 n − 1 n-1 n−1条边的 f f f值。剩下的边最多与20个点有关。 2 20 2^{20} 220枚举选点情况,在一个较低的复杂度内算出整棵树的答案即可。
-
如何实现计算? 我们发现 一个点 对于其父亲的转移方式是: f u , k = ∏ f s o n , k f_{u,k}=\prod f{son,k} fu,k=∏fson,k,这样对于 u u u的祖先 A n c Anc Anc, f A n c , 0 = K 0 ∗ f u , 0 + K 1 ∗ f u , 1 f_{Anc,0}=K_{0}*f_{u,0}+K_{1}*f_{u,1} fAnc,0=K0∗fu,0+K1∗fu,1,可以建出虚树,预处理出系数存在边上。由于建树的过程中一个点只会被访问到一次,暴力跳父亲就可以。(也可以用dfs实现建虚树)
-
于是我们用 O ( n + k ∗ 2 k ) O(n+k*2^k) O(n+k∗2k)复杂度愉快地 A C AC AC此题。
-
仔细一想,其实不过是用了虚树模拟了 D D P DDP DDP的过程。。所以用 D D P DDP DDP可以直接做,而且代码好些得多。。。
CODE
#include<bits/stdc++.h>
using namespace std;
#define sf scanf
#define ri register int
#define in red()
#define gc getchar()
#define cs const
#define ll long long
inline int red(){
int num=0,f=1;char c=gc;
for(;!isdigit(c);c=gc)if(c=='-')f=-1;
for(;isdigit(c);c=gc)num=(num<<1)+(num<<3)+(c^48);
return num*f;
}
cs int N=1e5+20,mod=998244353;
inline int add(cs int &a,cs int &b){return a+b>=mod ? a-mod+b : a+b;}
inline int dec(cs int &a,cs int &b){return a-b<0 ? a+mod-b : a-b;}
inline int mul(cs int &a,cs int &b){return 1ll*a*b%mod;}
inline void Add(int &a,cs int &b){a= a+b>=mod ? a-mod+b:a+b;}
inline void Mul(int &a,cs int &b){a= 1ll*a*b%mod;}
//PART1
int col[N],n,m;
inline int find(int u){
int tu,uu=u;
while(col[u])u=col[u];
while(uu^u){
tu=col[uu];
col[uu]=u;
uu=tu;
}
return u;
}
inline bool merge(int x,int y){
int fx=find(x),fy=find(y);
if(fx==fy)return 1;
col[fx]=fy;
return 0;
}
typedef pair<int,int> pi;
#define fi first
#define se second
pi e[N];
set<int>s;
vector<pi> V;
vector<int>P,g[N];
bool vis[N];
inline void build(){
for(ri i=1;i<=m;++i){
if(merge(e[i].fi,e[i].se))s.insert(e[i].fi),s.insert(e[i].se),V.push_back(e[i]);
else g[e[i].fi].push_back(e[i].se),g[e[i].se].push_back(e[i].fi);
}
}
int f[N][2],up,fa[N];
inline void dfs1(int u){
f[u][0]=1;f[u][1]=1;
for(ri i=g[u].size()-1;i>=0;--i){
int v=g[u][i];
if(v==fa[u])continue;
fa[v]=u;
dfs1(v);
Mul(f[u][0],add(f[v][0],f[v][1]));
Mul(f[u][1],f[v][0]);
}
}
//PART2
cs int M=100;
int del[N][2],k[M][2][2];
int to[M],nxt[M],cnt=0,head[N];
//dfs版建树
//fi: point->id se :edge-> id
vector<pi>h[N];
pi dfs3(int u) {
pi now;
del[u][0]=del[u][1]=1;
for(ri i=g[u].size()-1;i>=0;--i){
int v=g[u][i];
if(v==fa[u])continue;
now=dfs3(v);
if(now.fi)h[u].push_back(now);
else Mul(del[u][0],add(f[v][0],f[v][1])),Mul(del[u][1],f[v][0]);
}
if(!vis[u] && u^1 && h[u].size()<2){
if(h[u].size()){
int id=h[u][0].se;
int k00=mul(del[u][0],add(k[id][0][0],k[id][1][0])),k01=mul(del[u][0],add(k[id][0][1],k[id][1][1]));
int k10=mul(del[u][1],k[id][0][0]),k11=mul(del[u][1],k[id][0][1]);
k[id][0][0]=k00;k[id][0][1]=k01;
k[id][1][0]=k10;k[id][1][1]=k11;
return h[u][0];
}
else return pi(0,0);
}
else{
for(ri i=h[u].size()-1;i>=0;--i){
int id=h[u][i].se,v=h[u][i].fi;
nxt[id]=head[u];head[u]=id;to[id]=v;
}
++cnt;k[cnt][0][0]=1;k[cnt][1][1]=1;
return pi(u,cnt);
}
}
#define It set<int> :: iterator
int sta[N],r[N][2],ans=0;
int tot=0;
inline bool check(){
for(ri i=V.size()-1;i>=0;--i){
if(sta[V[i].fi]&sta[V[i].se])return 0;
}
return 1;
}
inline void dfs4(int u){
memset(r[u],0,sizeof(r[u]));
if(vis[u]){
if(!sta[u])r[u][0]=del[u][0];
else r[u][1]=del[u][1];
}
else memcpy(r[u],del[u],sizeof(del[u]));
int v0,v1;
for(ri i=head[u];i;i=nxt[i]){
int v=to[i];
dfs4(v);
v0=add(mul(k[i][0][0],r[v][0]),mul(k[i][0][1],r[v][1]));
v1=add(mul(k[i][1][0],r[v][0]),mul(k[i][1][1],r[v][1]));
Mul(r[u][0],add(v0,v1));
Mul(r[u][1],v0);
}
}
inline void dfs(int pos){
if(pos==up){
if(check())dfs4(1),Add(ans,add(r[1][0],r[1][1]));
return;
}
sta[P[pos]]=0;
dfs(pos+1);
sta[P[pos]]=1;
dfs(pos+1);
}
inline void solve(){
build();
dfs1(1);
if(m==n-1){
cout<<add(f[1][0],f[1][1]);
exit(0);
}
for(It t=s.begin();t!=s.end();++t)vis[*t]=1,P.push_back(*t);
dfs3(1);
up=P.size();
dfs(0);
cout<<ans<<'\n';
}
signed main(){
n=in;m=in;
for(ri i=1;i<=m;++i)e[i].fi=in,e[i].se=in;
solve();
return 0;
}