题意 给你一个图,求这个图的独立点集(集合内点互不相邻)个数。 \(n \leqslant 10^5,n-1 \leqslant m \leqslant n+10\) 题解 对于m=n-1的情况,直接上Tree_Dp即可, \(f_{u,1}=\prod (f_{v,0}+f_{v,1}),f_{u,0
题意
给你一个图,求这个图的独立点集(集合内点互不相邻)个数。
\(n \leqslant 10^5,n-1 \leqslant m \leqslant n+10\)
题解
对于m=n-1的情况,直接上Tree_Dp即可,\(f_{u,1}=\prod (f_{v,0}+f_{v,1}),f_{u,0}=\prod f_{v,1}\)。
然后我们发现m-n是个很小的数字,我们考虑可以先建出一个生成树,再考虑那么非树边。
非树边最多11条,我们可以直接枚举非树边两端点选或不选(实际上可以只枚举一端)。然后在树上直接Dp,复杂度\(O(2^{m-n+1}n)\)。然后你就有75分了。
尝试对这个算法进行优化。我们发现每次Dp都有很多重复的步骤(比如完全没有建在虚树里的子树),我们可以提前预处理。但是在虚树边上的点我们还是得一个一个枚举。但是我们又发现,对于每一个虚树边<u,v>,\(f_{v,1}\)对\(f_{u,0}\)的贡献只需要乘上一个固定的系数。具体来说,我们设\(f_{v,1}=x\),那么点v在虚树上暴力向上跳,并将周围的子树的贡献也乘进去,最终对\(f_{u,0}\)的贡献一定是乘上\(k_{<u,v>,0,1} \cdot x\)。所以我们对于每一个虚树边<u,v>以及u,v选或不选的每一种状态都预处理里出这个系数,然后枚举状态并在虚树上Dp,这样复杂度便是\(O(n+s \times 2^s)\)(s为非树边两端点的点集大小)。
#include<cmath> #include<cstdio> #include<cstring> #include<iostream> #include<algorithm> using namespace std; const int maxn=1e5; const int mod=998244353; int n,m,tot,cnt,Time,k; int a[maxn+8],b[maxn+8],fa[maxn+8]; int pre[maxn*2+8],now[maxn+8],son[maxn*2+8]; int jump[maxn*2+8][28],dfn[maxn+8],dep[maxn+8]; int tmp[2],g[maxn+8][2],res[maxn+8][2],val[maxn*2+8][2][2]; int st[maxn+8]; int f[maxn+8][2]; bool vis[maxn+8]; int read() { int x=0,f=1;char ch=getchar(); for (;ch<‘0‘||ch>‘9‘;ch=getchar()) if (ch==‘-‘) f=-1; for (;ch>=‘0‘&&ch<=‘9‘;ch=getchar()) x=x*10+ch-‘0‘; return x*f; } struct Disjoint_Set_Union { int fa[maxn+8]; int find(int x){return fa[x]==x?x:fa[x]=find(fa[x]);} }DSU; void add(int u,int v) { pre[++tot]=now[u]; now[u]=tot; son[tot]=v; } void dfs(int x) { jump[dfn[x]=++Time][0]=x; dep[x]=dep[fa[x]]+1; for (int p=now[x];p;p=pre[p]) { int child=son[p]; if (child==fa[x]) continue; fa[child]=x; dfs(child); jump[++Time][0]=x; } } int RMQ(int x,int y){return dep[x]<dep[y]?x:y;} void Build_St() { for (int j=1;j<=log(Time)/log(2);j++) for (int i=1;i<=Time-(1<<j)+1;i++) jump[i][j]=RMQ(jump[i][j-1],jump[i+(1<<(j-1))][j-1]); } bool cmp(int x,int y){return dfn[x]<dfn[y];} int Get_Lca(int x,int y) { if (dfn[x]>dfn[y]) swap(x,y); int len=dfn[y]-dfn[x]+1,t=log(len)/log(2); return RMQ(jump[dfn[x]][t],jump[dfn[y]-(1<<t)+1][t]); } void Make_F(int x,int fa) { f[x][0]=1,f[x][1]=1; for (int p=now[x];p;p=pre[p]) { int child=son[p]; if (child==fa) continue; Make_F(child,x); f[x][1]=1ll*f[x][1]*f[child][0]%mod; f[x][0]=1ll*(f[child][1]+f[child][0])%mod*f[x][0]%mod; } } int update(int x,int y,int i,int j) { tmp[i^1]=0,tmp[i]=1; while(x!=y) { vis[x]=1; for (int p=now[x];p;p=pre[p]) { int child=son[p]; if (vis[child]||child==fa[x]) continue; tmp[0]=1ll*(f[child][0]+f[child][1])%mod*tmp[0]%mod; tmp[1]=1ll*tmp[1]*f[child][0]%mod; } swap(tmp[0],tmp[1]); tmp[0]=(tmp[0]+tmp[1])%mod; x=fa[x]; } return tmp[j]; } void Get_Res(int x) { res[x][0]=1,res[x][1]=1; for (int p=now[x];p;p=pre[p]) { int child=son[p]; if (vis[child]||child==fa[x]) continue; vis[child]=1; res[x][0]=1ll*(f[child][0]+f[child][1])%mod*res[x][0]%mod; res[x][1]=1ll*res[x][1]*f[child][0]%mod; } } struct Virtual_Tree { int tot; int color[maxn+8]; int pre[maxn*2+8],now[maxn+8],son[maxn*2+8]; void add(int u,int v) { pre[++tot]=now[u]; now[u]=tot; son[tot]=v; } void insert(int u,int v){add(u,v),add(v,u);} void Make_Val(int x,int fa) { for (int p=now[x];p;p=pre[p]) { int child=son[p]; if (child==fa) continue; Make_Val(child,x); for (int i=0;i<2;i++) for (int j=0;j<2;j++) val[p][i][j]=update(child,x,i,j); } Get_Res(x); } void calc(int x,int fa) { if (color[x]==-1) g[x][0]=res[x][0],g[x][1]=res[x][1]; else g[x][color[x]]=res[x][color[x]],g[x][color[x]^1]=0; for (int p=now[x];p;p=pre[p]) { int child=son[p]; if (child==fa) continue; calc(child,x); for (int i=0;i<2;i++) g[x][i]=1ll*(1ll*g[child][0]*val[p][0][i]%mod+1ll*g[child][1]*val[p][1][i]%mod)*g[x][i]%mod; } } void solve() { int ans=0; Make_Val(n,0); memset(color,-1,sizeof(color)); for (int sta=0;sta<1<<k;sta++) { for (int i=1;i<=k;i++) color[a[i]]=1&(sta>>(i-1)); bool flag=0; for (int i=1;i<=cnt;i+=2) if (color[b[i]]&color[b[i+1]]) { flag=1; break; } if (flag) continue; calc(n,0); ans=(ans+g[n][0])%mod; } printf("%d\n",ans); } }VT; void solve() { sort(a+1,a+cnt+1,cmp); k=unique(a+1,a+cnt+1)-a-1; int tail=1; st[tail]=n; for (int i=1;i<=k;i++) { int Lca=Get_Lca(a[i],st[tail]),pre=0; while(dep[Lca]<dep[st[tail]]) { if (pre) VT.insert(pre,st[tail]); pre=st[tail--]; } if (pre) VT.insert(pre,Lca); if (Lca!=st[tail]) st[++tail]=Lca; st[++tail]=a[i]; } tail--; while(tail) VT.insert(st[tail],st[tail+1]),tail--; VT.solve(); } int main() { n=read(),m=read(); for (int i=1;i<=n;i++) DSU.fa[i]=i; for (int i=1;i<=m;i++) { int u=read(),v=read(); if (DSU.find(u)!=DSU.find(v)) { add(u,v),add(v,u); DSU.fa[DSU.find(u)]=DSU.find(v); } else { b[cnt+1]=u,b[cnt+2]=v; a[++cnt]=u,a[++cnt]=v; } } ++n; add(1,n),add(n,1); dfs(n); Build_St(); Make_F(n,0); solve(); return 0; }