題目描述:
如題,給出一棵樹以及額外的條邊,求選出一個獨立集的方案數。
題目分析:
如果,那麼直接設表示選或不選DP。
現在,直接DP得出的方案可能有些不滿足限制。但是很小,條邊連接着個特殊點。
如果暴力枚舉這個特殊點的選法(強制其選或不選),再進行DP,可以得到的做法。
實際上不需要,每條邊最多隻有3個狀態,可以壓縮爲,進一步地,第一輪強制選不選,第二輪強制不選,可以壓縮爲。於是就有了的做法。(常數小的選手可能可以大力rush過掉,比如這份 submission)
每次只有22個點的狀態發生變化,但每次都要的DP,顯然有些浪費。
考慮這個點把樹分成了若干連通塊,如果每個連通塊只受一或兩個特殊點影響,或許可以用一些預處理轉移係數的方法。
使用虛樹,每個連通塊要麼對應虛樹上的一條邊,要麼是在虛樹某個葉節點的子樹中。
對於非虛樹邊上的貢獻可以預處理,而虛樹上的轉移邊可以寫作的形式,是轉移係數,同樣可以預處理。
於是複雜度就是
Code:
#include<bits/stdc++.h>
#define maxn 100015
using namespace std;
const int mod = 998244353;
int n,m,cnt,ans,siz[maxn],dfn[maxn],tim;//siz means number of key points in different subtrees.
bool key[maxn],vis[maxn];
int fir[maxn],nxt[maxn<<1],to[maxn<<1],tot;
void line(int x,int y){nxt[++tot]=fir[x],fir[x]=tot,to[tot]=y;}
struct edge{int x,y;}e[12];
void dfs(int u,int ff){
dfn[u]=++tim;
for(int i=fir[u],v;i;i=nxt[i]) if((v=to[i])!=ff){
if(!dfn[v]) dfs(v,u),siz[u]+=siz[v];
else if(dfn[v]<dfn[u]) key[u]=key[v]=1,e[++cnt]=(edge){u,v};
}
key[u]|=siz[u]>=2,siz[u]=siz[u]||key[u];
}
#define fi first
#define se second
typedef pair<int,int> pii;
struct node{int v; pii k0,k1;};
vector<node>G[maxn];
int f[maxn][2],F[maxn][2],fix[maxn][2];
pii k[maxn][2];
pii operator + (const pii &a,const pii &b){return pii((a.fi+b.fi)%mod,(a.se+b.se)%mod);}
pii operator * (const pii &a,const int &t){return pii(1ll*a.fi*t%mod,1ll*a.se*t%mod);}
int build(int u){
f[u][0]=f[u][1]=1,vis[u]=1;
int son=0;
for(int i=fir[u],v;i;i=nxt[i]) if(!vis[v=to[i]]){
int now=build(v);
if(!now) f[u][0]=1ll*f[u][0]*(f[v][0]+f[v][1])%mod,f[u][1]=1ll*f[u][1]*f[v][0]%mod;
else if(!key[u]) k[u][0]=k[v][0]+k[v][1],k[u][1]=k[v][0],son=now;
else G[u].push_back((node){now,k[v][0]+k[v][1],k[v][0]});
}
if(!key[u]) {if(son) k[u][0]=k[u][0]*f[u][0],k[u][1]=k[u][1]*f[u][1];}
else k[u][0]=pii(1,0),k[u][1]=pii(0,1),son=u;
return son;
}
void solve(int u){
F[u][0]=fix[u][1]?0:f[u][0], F[u][1]=fix[u][0]?0:f[u][1];
for(int i=0,lim=G[u].size(),v;i<lim;i++){
solve(v=G[u][i].v); pii k0=G[u][i].k0,k1=G[u][i].k1;
F[u][0]=1ll*F[u][0]*((1ll*k0.fi*F[v][0]+1ll*k0.se*F[v][1])%mod)%mod;
F[u][1]=1ll*F[u][1]*((1ll*k1.fi*F[v][0]+1ll*k1.se*F[v][1])%mod)%mod;
}
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=1,x,y;i<=m;i++) scanf("%d%d",&x,&y),line(x,y),line(y,x);
dfs(1,0),key[1]=1,build(1);
for(int s=0;s<1<<cnt;s++){
for(int i=1;i<=cnt;i++) fix[e[i].x][0]=fix[e[i].x][1]=fix[e[i].y][0]=fix[e[i].y][1]=0;
for(int i=1;i<=cnt;i++)
if(s>>i-1&1) fix[e[i].x][1]=fix[e[i].y][0]=1;
else fix[e[i].x][0]=1;
solve(1),ans=(1ll*ans+F[1][0]+F[1][1])%mod;
}
printf("%d\n",ans);
}