duliu
点就完了
因为标签是虚树所以考虑树
看到5~6的数据点,发现图是棵树,于是快乐地把5~6数据点作为切入点
其实就是么有上司的舞会是吧
\[f[x][0]=Σf[v][0]+f[v][1] \]
\[f[x][1]=Σf[v][0] \]
那么我们对于一个图就直接地把图破成树好了,枚举每条边是否选择,强行跑树DP,但是边太多了所以必死无疑
但是考虑标签是虚树但是看到数据,\(m\)只比\(n\)大\(22\),通过虚树可以让枚举次数大大降低,另外,因为每次只会有一些特殊的点(就是环上的点)对答案做出贡献,可以用虚树来优化
而最大的\(m \leq n+11\),我们可以把这些多出来的点拎出来构建虚树,并在虚树上DP
注意,接下来是本题最恶心的地方
我们假设有下面这条链(这条链将被用来建立虚树,我们会在这条边上DP)
dp数组向上传递是有一定规律的
\[f[5][0]=πf[6][0]+f[6][1] \]
\[f[5][1]=πf[6][0] \]
\[f[4][0]=πf[5][0]+f[5][1] \]
\[即 \]
\[f[4][0]=π(f[6][0]+f[6][1])+f[6][0] \]
\[f[4][1]=πf[6][0]+f[6][1] \]
\[f[3][0]=πf[4][0]+f[4][1] \]
\[f[3][0]=π((f[6][0]+f[6][1])+f[6][0])+(f[6][0]+f[6][1]) \]
\[f[3][1]=πf[4][0] \]
\[f[3][1]=πf[6][0]+f[6][1]+f[6][0] \]
我们发现
\[f[6][0] 和 f[6][1] \]
每次递归会传递一次这玩意,而虚树边上的点是固定的,所以这两个玩意被加上的次数是可以递归处理出来的
所以我们得到这个方程(v是x在虚树上的父亲)
\[f[x][1]=k0[v][1]*f[v][0]+k1[v][1]*f[v][1] \]
\[f[x][0]=k0[v][0]*f[v][0]+k1[v][0]*f[v][0] \]
预处理出k就能舒服DP
而预处理的方法比较坑
我们先考虑x原树上的一个儿子t
设在添加t(树DP的时候)前\(f[x][1]\)为\(ovo[x][1]\)
\[f[x][1]=ovo[x][1]*[(f[v][0]*k1[v][0])+(f[v][1]*k0[v][1])] \]
所以考虑刚才对k数组的理解
\[k1[v][0]=ovo[x][1]*k0[v][0] \]
最后梳理下dp的流程
预处理出k数组
每次暴力枚举加入了哪些非树边
树DP
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define ll long long
#define N 100020
#define mod 998244353
using namespace std;
ll head[N],hh[N],dfn[N],dep[N],f[25][N],ff[N][3],k0[N][3],k1[N][3],dp[N][3];
ll b[25],bj[N],in[N],g[N][3],p[N],st[N];
ll n,m,cnt=1,ret,tmp,T,tt,tp,ans;
bool vis[N],en[N*2];
struct Tuck{
ll u1,v1;
}tr[N];
struct The_World{
ll to,nxt;
}q[N*10];
struct Star_Platinum{
ll v,nx;
}e[N*10];
bool cmp(ll x,ll y){
return dfn[x]<dfn[y];
}
void add(ll u,ll v){
q[++cnt].to=v,q[cnt].nxt=head[u];
head[u]=cnt;
q[++cnt].to=u,q[cnt].nxt=head[v];
head[v]=cnt;
}
void edd(ll u,ll v){
e[++ret].v=v,e[ret].nx=hh[u];
hh[u]=ret;
}
void dfs(ll x,ll fa){
dfn[x]=++tmp;
dep[x]=dep[fa]+1;
vis[x]=1;
for(ll i=1;i<=20&&f[i-1][x];i++){
f[i][x]=f[i-1][f[i-1][x]];
}
for(ll i=head[x];i;i=q[i].nxt){
ll v=q[i].to;
if(v!=fa){
if(!vis[v]){
f[0][v]=x;
dfs(v,x);
}
else {
T++;
tr[T].u1=x,tr[T].v1=v;
en[i]=en[i^1]=1;
}
}
}
}
ll LYCA(ll x,ll y){
if(dep[x]<dep[y])swap(x,y);
for(ll i=20;i>=0;i--){
if(dep[f[i][x]]>=dep[y]){
x=f[i][x];
}
if(x==y)return x;
}
for(ll i=20;i>=0;i--){
if(f[i][x]!=f[i][y]){
x=f[i][x];
y=f[i][y];
}
}
return f[0][x];
}
void build_vtree(ll x){
if(tt==1){
st[++tt]=x;
return ;
}
ll lca=LYCA(st[tt],x);
while(tt>1&&dfn[st[tt-1]]>=dfn[lca]){
edd(st[tt-1],st[tt]);
tt--;
}
if(st[tt]!=lca){
in[lca]=1;
edd(lca,st[tt]);
st[tt]=lca;
}
st[++tt]=x;
}
void dp1(ll x,ll o){
ff[x][0]=ff[x][1]=1;
in[x]=1;
for(ll i=head[x];i;i=q[i].nxt){
ll v=q[i].to;
if(v==f[0][x]||v==o||in[v]||en[i])continue;
dp1(v,o);
ff[x][0]=ff[x][0]*(ff[v][0]+ff[v][1])%mod;
ff[x][1]=ff[x][1]*ff[v][0]%mod;
}
}
void getk(ll x,ll y){
k0[x][0]=1,k1[x][1]=1;
for(ll i=x;f[0][i]!=y;i=f[0][i]){
dp1(f[0][i],i);
ll t0=k0[x][0],t1=k1[x][0];
k0[x][0]=ff[f[0][i]][0]*(k0[x][0]+k0[x][1])%mod;
k1[x][0]=ff[f[0][i]][0]*(k1[x][0]+k1[x][1])%mod;
k0[x][1]=ff[f[0][i]][1]*t0%mod;
k1[x][1]=ff[f[0][i]][1]*t1%mod;
}
}
void yu_work(ll x){
for(ll i=hh[x];i;i=e[i].nx){
ll v=e[i].v;
yu_work(v),getk(v,x);
}
ff[x][0]=ff[x][1]=1;
for(ll i=head[x];i;i=q[i].nxt){
ll v=q[i].to;
if(in[v]||en[i]||v==f[0][x])continue;
dp1(v,0);
ff[x][0]=ff[x][0]*(ff[v][0]+ff[v][1])%mod;
ff[x][1]=ff[x][1]*ff[v][0]%mod;
}
}
void DP2(ll x){
g[x][0]=ff[x][0],g[x][1]=ff[x][1];
for(ll i=hh[x];i;i=e[i].nx){
ll v=e[i].v;
if(v!=f[0][x]){
DP2(v);
ll f0=(k0[v][0]*g[v][0]%mod+k1[v][0]*g[v][1]%mod)%mod;
ll f1=(k0[v][1]*g[v][0]%mod+k1[v][1]*g[v][1]%mod)%mod;
g[x][0]=g[x][0]*(f0+f1)%mod;
g[x][1]=g[x][1]*f0%mod;
}
}
if(bj[x]==1)g[x][0]=0;
if(bj[x]==-1)g[x][1]=0;
}
int main(){
scanf("%lld%lld",&n,&m);
for(ll i=1;i<=m;i++){
ll a1,a2;
scanf("%lld%lld",&a1,&a2);
add(a1,a2);
}
dfs(1,0);
for(ll i=1;i<=T;i++){
if(!in[tr[i].u1])in[tr[i].u1]=1,p[++tp]=tr[i].u1;
if(!in[tr[i].v1])in[tr[i].v1]=1,p[++tp]=tr[i].v1;
}
sort(p+1,p+1+tp,cmp);
st[tt=1]=1;
for(ll i=1;i<=tp;i++){
if(p[i]==1)continue;
build_vtree(p[i]);
}
while(tt>1){
edd(st[tt-1],st[tt]);
tt--;
}
yu_work(1);
b[1]=1;
for(ll i=2;i<=tp+1;i++)b[i]=b[i-1]*2;
for(ll i=0;i<b[tp+1];i++){
for(ll j=1;j<=tp;j++){
if(i&b[j])bj[p[j]]=1;
else bj[p[j]]=-1;
}
bool pool=0;
for(ll j=1;j<=T;j++){
if(bj[tr[j].u1]==1&&bj[tr[j].v1]==1){
pool=1;break;
}
}
if(pool)continue;
DP2(1);
ans=(ans+(g[1][0]+g[1][1])%mod)%mod;
}
printf("%lld",ans);
}