【题目】
BZOJ
luogu
求图的最大独立集方案数。 n ≤ 1 0 5 , n − 1 ≤ m ≤ n + 10 n\leq 10^5,n-1\leq m\leq n+10 n≤105,n−1≤m≤n+10
【解题思路】
非树边只有 11 11 11条。
树的最大独立集可以用一个朴素的最大独立集来做,我们枚举非树边中高的一点的状态,每种状态做一次 DP \text{DP} DP,可以做到 O ( 2 m − n + 1 n ) O(2^{m-n+1}n) O(2m−n+1n)
不难想到一类树上 NP \text{NP} NP问题的套路——动态 DP \text{DP} DP,于是这道题可以做到 O ( 2 m − n + 1 log n + n log n ) O(2^{m-n+1}\log n+n\log n) O(2m−n+1logn+nlogn),这里需要用 DFS \text{DFS} DFS去枚举状态。
实际上由于转移矩阵中很多都不会受到影响,我们可以考虑建出虚树,在虚树上进行 DP \text{DP} DP,就可以做到 O ( n + S ⋅ 2 m − n + 1 ) O(n+S\cdot 2^{m-n+1}) O(n+S⋅2m−n+1),这里 S S S是虚树上节点个数。
【参考代码】
#include
#define fi first
#define se second
#define mkp make_pair
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
const int N=1e5+10,M=100,mod=998244353;
int n,m,ans;
int read()
{
int ret=0;char c=getchar();
while(!isdigit(c)) c=getchar();
while(isdigit(c)) ret=ret*10+(c^48),c=getchar();
return ret;
}
namespace Graph
{
struct Tway{int v,nex;};
struct G
{
int tot,head[N];Tway e[N<<1];
void add(int u,int v){e[++tot]=(Tway){v,head[u]};head[u]=tot;}
};
}
using namespace Graph;
namespace Tree
{
G T,vt;
int ind,top,K,cnt;
int dep[N],pos[N],fa[19][N],fc[32],st[M],p[M];
int f[N][2],g[N][2],k[N][2][2],c[N];
bool invt[N],vis[N],fg[N<<1];
pii a[M];
void dfs(int x,int ff)
{
pos[x]=++ind;dep[x]=dep[ff]+1;fa[0][x]=ff;vis[x]=1;
for(int i=1;i<18;++i) fa[i][x]=fa[i-1][fa[i-1][x]];
for(int i=T.head[x];i;i=T.e[i].nex)
{
int v=T.e[i].v;
if(v==ff) continue;
if(!vis[v]) dfs(v,x);
else a[++K]=mkp(x,v),fg[i]=fg[i^1]=1;
}
}
int lca(int x,int y)
{
if(dep[x]<dep[y]) swap(x,y);
for(int t=dep[x]-dep[y],i=0;i<17;++i)
if(t&fc[i]) x=fa[i][x];
for(int i=17;~i;--i) if(fa[i][x]^fa[i][y])
x=fa[i][x],y=fa[i][y];
return x==y?x:fa[0][x];
}
void init()
{
n=read();m=read();T.tot=1;
fc[0]=1;for(int i=1;i<30;++i) fc[i]=fc[i-1]<<1;
for(int i=1,u,v;i<=m;++i) u=read(),v=read(),T.add(u,v),T.add(v,u);
}
bool cmp(int x,int y){return pos[x]<pos[y];}
void buildvt()
{
for(int i=1;i<=K;++i)
{
if(!invt[a[i].fi]) invt[a[i].fi]=1,p[++cnt]=a[i].fi;
if(!invt[a[i].se]) invt[a[i].se]=1,p[++cnt]=a[i].se;
}
sort(p+1,p+cnt+1,cmp);
if(!invt[1]) invt[1]=1,st[++top]=1;
for(int i=1;i<=cnt;++i)
{
int x=p[i],t=0;
for(;top;)
{
t=lca(st[top],x);
if(top>1 && dep[st[top-1]]>dep[t]) vt.add(st[top-1],st[top]),--top;
else if(dep[st[top]]>dep[t]) {vt.add(t,st[top]),--top;break;}//
else break;
}
if(st[top]^t) invt[t]=1,st[++top]=t;
st[++top]=x;
}
while(top>1) vt.add(st[top-1],st[top]),--top;
}
void predp(int x,int ban)
{
f[x][0]=f[x][1]=1;invt[x]=1;
for(int i=T.head[x];i;i=T.e[i].nex)
{
int v=T.e[i].v;
if(invt[v] || fg[i] || v==fa[0][x] || v==ban) continue;
predp(v,ban);
f[x][0]=(ll)f[x][0]*(f[v][0]+f[v][1])%mod;
f[x][1]=(ll)f[x][1]*f[v][0]%mod;
}
}
void getmat(int x,int y)
{
k[x][0][0]=k[x][1][1]=1;
for(int i=x;fa[0][i]^y;i=fa[0][i])
{
predp(fa[0][i],i);
int t0=k[x][0][0],t1=k[x][1][0],ff=fa[0][i];
k[x][0][0]=(ll)f[ff][0]*(k[x][0][0]+k[x][0][1])%mod;
k[x][1][0]=(ll)f[ff][0]*(k[x][1][0]+k[x][1][1])%mod;
k[x][0][1]=(ll)f[ff][1]*t0%mod;
k[x][1][1]=(ll)f[ff][1]*t1%mod;
}
}
void prework(int x)
{
//cerr<
for(int i=vt.head[x];i;i=vt.e[i].nex) prework(vt.e[i].v),getmat(vt.e[i].v,x);
f[x][0]=f[x][1]=1;
for(int i=T.head[x];i;i=T.e[i].nex)
{
int v=T.e[i].v;
if(invt[v] || fg[i] || v==fa[0][x]) continue;
predp(v,0);
f[x][0]=(ll)f[x][0]*(f[v][0]+f[v][1])%mod;
f[x][1]=(ll)f[x][1]*f[v][0]%mod;
}
}
void dp(int x)
{
g[x][0]=f[x][0];g[x][1]=f[x][1];
//printf("%d %d\n",f[x][0],f[x][1]);
for(int i=vt.head[x];i;i=vt.e[i].nex)
{
int v=vt.e[i].v;dp(v);
int f0=((ll)k[v][0][0]*g[v][0]+(ll)k[v][1][0]*g[v][1])%mod;
int f1=((ll)k[v][0][1]*g[v][0]+(ll)k[v][1][1]*g[v][1])%mod;
g[x][0]=(ll)g[x][0]*(f0+f1)%mod;g[x][1]=(ll)g[x][1]*f0%mod;
}
if(c[x]==1) g[x][0]=0;
if(c[x]==-1) g[x][1]=0;
}
void solve()
{
dfs(1,0);buildvt();
//cerr<
prework(1);
for(int S=0;S<fc[cnt];++S)
{
for(int i=1;i<=cnt;++i) c[p[i]]=(S&fc[i-1])?1:-1;
bool flag=0;
for(int i=1;i<=K;++i) if(c[a[i].fi]==1 && c[a[i].se]==1) {flag=1;break;}
if(flag) continue;
dp(1);ans=((ll)ans+g[1][0]+g[1][1])%mod;
}
printf("%d\n",ans);
}
}
int main()
{
#ifndef ONLINE_JUDGE
freopen("BZOJ5287.in","r",stdin);
freopen("BZOJ5287.out","w",stdout);
#endif
Tree::init();Tree::solve();
return 0;
}
这里把shadowice1984 的动态DP也贴上
#include
#include
using namespace std;const int N=1e5+10;typedef long long ll;const ll mod=998244353;
inline ll po(ll a,ll p){ll r=1;for(;p;p>>=1,a=a*a%mod)if(p&1)r=r*a%mod;return r;}
struct nod//用来做除法的结构体
{
ll v1;int c1;ll v2;int c2;
inline void ins(const ll& t1,const ll& t2)
{(v1*=(t1)?t1:1)%=mod;c1+=(t1==0);(v2*=(t2)?t2:1)%=mod;c2+=(t2==0);}
inline void del(const ll& t1,const ll& t2)
{
(v1*=(t1)?po(t1,mod-2):1)%=mod;c1-=(t1==0);
(v2*=(t2)?po(t2,mod-2):1)%=mod;c2-=(t2==0);
}
};
struct mar//矩阵
{
ll mp[2][2];
inline ll* operator [](const int& x){return mp[x];}
friend mar operator *(mar a,mar b)
{
mar c;
c[0][0]=((ll)a[0][0]*b[0][0]+(ll)a[0][1]*b[1][0])%mod;
c[0][1]=((ll)a[0][0]*b[0][1]+(ll)a[0][1]*b[1][1])%mod;
c[1][0]=((ll)a[1][0]*b[0][0]+(ll)a[1][1]*b[1][0])%mod;
c[1][1]=((ll)a[1][0]*b[0][1]+(ll)a[1][1]*b[1][1])%mod;return c;
}
void operator =(const nod& b)
{mp[1][0]=mp[0][0]=(b.c1)?0:b.v1;mp[0][1]=(b.c2)?0:b.v2;}
};
int v[2*N];int x[2*N];int ct;int al[N];int n;int m;ll ans;
int eu[22];int ev[22];int hd;int siz[N];int h[N];int mi[N];
inline void add(int u,int V){v[++ct]=V;x[ct]=al[u];al[u]=ct;}
struct bcj//智商不够数据结构来凑,随手敲了个并查集判非树边
{
int fa[N];
inline int f(int x){return fa[x]=(x==fa[x])?x:f(fa[x]);}
inline void u(int x,int y){fa[f(x)]=f(y);}
inline void ih(){for(int i=1;i<=n;i++)fa[i]=i;}
inline void ins(int U,int V)
{if(f(U)==f(V))eu[++hd]=U,ev[hd]=V;else add(U,V),add(V,U),u(U,V);}
}ufs;
inline int dfs1(int u,int f)//轻重链剖分
{
for(int i=al[u];i;i=x[i])
if(v[i]!=f)siz[u]+=dfs1(v[i],u),h[u]=(siz[h[u]]<siz[v[i]])?v[i]:h[u];return ++siz[u];
}
struct global_biased_tree//全局平衡二叉树
{
mar mul_bas[N][22];mar we_bas[N][22];nod ld_bas[N][22];int s[N][2];
mar* nmul[N];mar* nwe[N];nod* nld[N];//为了写的爽每个点开了个指针模拟栈
int st[N];int tp;int wsiz[N];int rot;int fa[N];//define n连实现栈
# define mul(p) (*nmul[p])
# define we(p) (*nwe[p])
# define incm(p) (*(nmul[p]+1)=*nmul[p],++nmul[p])
# define decm(p) (--nmul[p])
# define incw(p) (*(nwe[p]+1)=*nwe[p],++nwe[p])
# define decw(p) (--nwe[p])
# define ld(p) (*nld[p])
# define incl(p) (*(nld[p]+1)=*nld[p],++nld[p])
# define decl(p) (--nld[p])
inline void ud(int p){mul(p)=mul(s[p][0])*we(p)*mul(s[p][1]);}//更新
inline void ih()//初始化指针
{
for(int i=0;i<=n;i++)nmul[i]=mul_bas[i];for(int i=0;i<=n;i++)nwe[i]=we_bas[i];
for(int i=0;i<=n;i++)nld[i]=ld_bas[i];
for(int i=0;i<=21;i++)mul_bas[0][i][0][0]=1,mul_bas[0][i][1][1]=1;
}
inline int sbuild(int l,int r)//每条重链建bst
{
if(l>r)return 0;
int mid=l;int sum=0;for(int i=l;i<=r;i++)sum+=wsiz[st[i]];
for(int pre=0;(pre<<1)<sum;mid++)pre+=wsiz[st[mid]];mid--;
s[st[mid]][0]=sbuild(l,mid-1);fa[s[st[mid]][0]]=st[mid];
s[st[mid]][1]=sbuild(mid+1,r);fa[s[st[mid]][1]]=st[mid];ud(st[mid]);
return st[mid];
}
inline int solve(int u,int f)//链分治
{
for(int p=u,nf=f;p;nf=p,p=h[p])
{
wsiz[p]++;ld(p).v1++;ld(p).v2++;
for(int j=al[p];j;j=x[j])
if(v[j]!=h[p]&&v[j]!=nf)
{
int ve=solve(v[j],p);fa[ve]=p;wsiz[p]+=siz[v[j]];
ld(p).ins((mul(ve)[0][0]+mul(ve)[0][1])%mod,mul(ve)[0][0]);
}we(p)=ld(p);
}tp=0;for(int p=u;p;p=h[p])st[++tp]=p;reverse(st+1,st+tp+1);return sbuild(1,tp);
}
inline void modify(int u)//修改
{
incw(u);incl(u);ld(u).v1=0;we(u)=ld(u);
for(int p=u;p;p=fa[p])
if(fa[p]&&s[fa[p]][0]!=p&&s[fa[p]][1]!=p)
{
int f=fa[p];incw(f);incl(f);
ld(f).del((mul(p)[0][0]+mul(p)[0][1])%mod,mul(p)[0][0]);incm(p),ud(p);
ld(f).ins((mul(p)[0][0]+mul(p)[0][1])%mod,mul(p)[0][0]);we(f)=ld(f);
}else incm(p),ud(p);
}
inline void undo(int u)//撤销
{
decw(u);decl(u);
for(int p=u;p;p=fa[p])
if(fa[p]&&s[fa[p]][0]!=p&&s[fa[p]][1]!=p)decw(fa[p]),decl(fa[p]),decm(p);
else decm(p);
}
inline ll calc(){return (mul(rot)[0][0]+mul(rot)[0][1])%mod;}
}gbt;
inline void dfs(int stp,int siz)//dfs+容斥
{
if(stp==hd+1)
{
if(siz&1)(ans+=mod-gbt.calc())%=mod;
else (ans+=gbt.calc())%=mod;return;
}dfs(stp+1,siz);
if(!mi[eu[stp]]){mi[eu[stp]]=stp;gbt.modify(eu[stp]);}
if(!mi[ev[stp]]){mi[ev[stp]]=stp;gbt.modify(ev[stp]);}
dfs(stp+1,siz+1);
if(mi[ev[stp]]==stp){mi[ev[stp]]=0;gbt.undo(ev[stp]);}
if(mi[eu[stp]]==stp){mi[eu[stp]]=0;gbt.undo(eu[stp]);}
}
int main()
{
scanf("%d%d",&n,&m);ufs.ih();
for(int i=1,u,v;i<=m;i++)scanf("%d%d",&u,&v),ufs.ins(u,v);dfs1(1,0);
gbt.ih();gbt.rot=gbt.solve(1,0);dfs(1,0);printf("%lld",ans);return 0;//拜拜程序~
}