VII.[HNOI/AHOI2018]毒瘤
题如其名
先说一下我的思路:跑出任一生成树,关于非树边的点集建虚树,然后在虚树上跑状压DP。非树边最多有 \(11\) 条,则非树边点集最大是 \(22\),则虚树大小最大 \(43\),因此状压DP复杂度是 \(43\times2^{43}\),显然不可能通过,得分 \(55\%\)。
然后,只能转换思路。发现,任一生成树好像没有太多性质,我们不如用一种特定的生成树——dfs
树。
dfs
树有什么性质呢?非树边只有可能是返祖边,不存在横叉边。
我们考虑枚举每条非树边两边端点的状态是 \((0,0)\),\((1,0)\) 还是 \((0,1)\),然后剩下用暴力树形DP解决,则此时复杂度是以 \(3\) 为底的;但是,当我们使用 dfs
树的时候,就会发现我们只需要枚举返祖边中祖先节点的状态;若祖先节点是 \(1\),则子孙节点必为 \(0\);而若祖先节点是 \(0\),子孙节点便没有限制,也就是说其可以被看作是普通节点一样转移。这样复杂度便优化到了 \(n2^{m-n+1}\)。
现在考虑建出虚树。对于每个点(注意不一定是虚树节点),我们维护 \(g_{x,0/1}\) 表示关于虚树外(这里的虚树外指所有子树中没有虚树节点的节点)的部分,点 \(x\) 不选/选的方案数;对于虚树上每条边,我们维护 \(h_{x,y,0/1,0/1}\) 表示 \(x\) 不选/选,\(y\) 不选/选时,这条边的方案数(注意到虚树上一条边在实树上可能是一条路径,并且路径上节点还可能连有其它不在虚树上的子树)。则,\(g\) 显然可以一遍DP得出,\(h\) 可以通过 \(g\) 数组得出。然后,枚举非树边后的DP环节就可以在虚树上进行,复杂度 \(O(m-n+1)\)。若设 \(N=m-n+1\) 的话,复杂度即为 \(n+N2^N\)。
代码:
#include<bits/stdc++.h>
using namespace std;
const int mod=998244353;
int n,m,dsu[100100],dep[100100],anc[100100][20],dfn[100100],tot,f[110][2],g[100100][2],id[100100],cnt,h[100][100][2][2],img[100],fx[100],res;
vector<int>v[100100],sp,w[100];
vector<pair<int,int> >u;
bool vis[100100];
void dfs(int x,int fa){
vis[x]=true,anc[x][0]=fa,dep[x]=dep[fa]+1,dfn[x]=++tot;
for(int i=1;i<20;i++)anc[x][i]=anc[anc[x][i-1]][i-1];
for(auto i=v[x].begin();i!=v[x].end();){
if(*i==fa){i++;continue;}
if(vis[*i]){
if(dep[*i]<dep[x])sp.push_back(x),sp.push_back(*i),u.push_back(make_pair(*i,x));
i=v[x].erase(i);
}else dfs(*i,x),i++;
}
}
int LCA(int x,int y){
if(dep[x]>dep[y])swap(x,y);
for(int i=19;i>=0;i--)if(dep[x]<=dep[y]-(1<<i))y=anc[y][i];
if(x==y)return x;
for(int i=19;i>=0;i--)if(anc[x][i]!=anc[y][i])x=anc[x][i],y=anc[y][i];
return anc[x][0];
}
int stk[100],tp,ps[100100];
void ins(int x){
id[x]=++cnt,img[cnt]=x;
if(!tp){stk[++tp]=x;return;}
int lca=LCA(stk[tp],x);if(!id[lca])id[lca]=++cnt,img[cnt]=lca;
while(tp&&dep[stk[tp-1]]>=dep[lca])w[id[stk[tp-1]]].push_back(id[stk[tp]]),tp--;
if(tp&&dep[stk[tp]]>dep[lca])w[id[lca]].push_back(id[stk[tp--]]);
if(stk[tp]!=lca)stk[++tp]=lca;
stk[++tp]=x;
}
int dfs2(int x,int fa){
g[x][0]=g[x][1]=1;
for(auto y:v[x])if(y!=fa){
if(dfs2(y,x)){ps[x]=ps[y];continue;}
g[x][0]=1ll*g[x][0]*(g[y][0]+g[y][1])%mod;
g[x][1]=1ll*g[x][1]*g[y][0]%mod;
}
if(id[x])ps[x]=id[x];
return ps[x];
}
void dfs3(int x,int fa,int rt){
// printf("(%d,%d:%d)\n",x,fa,rt);
if(id[x]){h[rt][id[x]][0][0]=h[rt][id[x]][0][1]=h[rt][id[x]][1][0]=1;return;}
for(auto y:v[x]){
if(!ps[y]||y==fa)continue;
dfs3(y,x,rt);
int tmp[2]={h[rt][ps[y]][0][0],h[rt][ps[y]][0][1]};
h[rt][ps[y]][0][0]=(1ll*tmp[0]*g[x][0]+1ll*h[rt][ps[y]][1][0]*g[x][1])%mod;
h[rt][ps[y]][0][1]=(1ll*tmp[1]*g[x][0]+1ll*h[rt][ps[y]][1][1]*g[x][1])%mod;
h[rt][ps[y]][1][0]=1ll*tmp[0]*g[x][0]%mod;
h[rt][ps[y]][1][1]=1ll*tmp[1]*g[x][0]%mod;
}
}
void dfs4(int x){
f[x][0]=g[img[x]][0],f[x][1]=g[img[x]][1];
for(auto y:w[x]){
dfs4(y);
// printf("(%d,%d):[%d,%d],[%d,%d]:(%d,%d,%d,%d)\n",x,y,f[x][0],f[x][1],f[y][0],f[y][1],h[x][y][0][0],h[x][y][0][1],h[x][y][1][0],h[x][y][1][1]);
f[x][0]=1ll*(1ll*f[y][0]*h[x][y][0][0]+1ll*f[y][1]*h[x][y][0][1])%mod*f[x][0]%mod;
f[x][1]=1ll*(1ll*f[y][0]*h[x][y][1][0]+1ll*f[y][1]*h[x][y][1][1])%mod*f[x][1]%mod;
}
if(fx[x]!=-1)f[x][!fx[x]]=0;
// printf("%d[%d]:(%d,%d)\n",x,img[x],f[x][0],f[x][1]);
}
int main(){
scanf("%d%d",&n,&m);
for(int i=1,x,y;i<=m;i++)scanf("%d%d",&x,&y),v[x].push_back(y),v[y].push_back(x);
dfs(1,0);
// puts("");for(int i=1;i<=n;i++)for(auto j:v[i])if(j<i)printf("%d %d\n",i,j);
if(sp.empty()){dfs2(1,0);printf("%d\n",(g[1][0]+g[1][1])%mod);return 0;}
sort(sp.begin(),sp.end(),[](int u,int v){return dfn[u]<dfn[v];}),sp.resize(unique(sp.begin(),sp.end())-sp.begin());
// for(auto i:sp)printf("%d ",i);puts("");
if(sp[0]!=1)stk[++tp]=1,id[1]=++cnt,img[cnt]=1;
for(auto i:sp)ins(i);
while(tp>=2)w[id[stk[tp-1]]].push_back(id[stk[tp]]),tp--;
// for(int i=1;i<=n;i++)printf("%d ",id[i]);puts("");
// for(int i=1;i<=cnt;i++)printf("%d ",img[i]);puts("");
// puts("");for(auto i:u)printf("%d %d\n",i.first,i.second);puts("");
dfs2(1,0);
// for(int i=1;i<=n;i++)printf("[%d,%d]\n",g[i][0],g[i][1]);
// for(int i=1;i<=n;i++)printf("%d ",ps[i]);puts("");
for(int i=1;i<=cnt;i++)for(auto j:v[img[i]])if(dep[j]>dep[img[i]])dfs3(j,img[i],i);
// for(int i=1;i<=cnt;i++)for(auto j:w[i])printf("[%d,%d][%d,%d]:%d %d %d %d\n",i,j,img[i],img[j],h[i][j][0][0],h[i][j][0][1],h[i][j][1][0],h[i][j][1][1]);
for(auto&i:u)i.first=id[i.first],i.second=id[i.second];
memset(fx,-1,sizeof(fx));
for(int i=0;i<(1<<u.size());i++){
bool ok=true;
for(int j=0;j<u.size();j++)if((i>>j)&1){
if(fx[u[j].first]==0){ok=false;break;}
fx[u[j].first]=1;
if(fx[u[j].second]==1){ok=false;break;}
fx[u[j].second]=0;
}else{
if(fx[u[j].first]==1){ok=false;break;}
fx[u[j].first]=0;
}
// for(int i=1;i<=cnt;i++)printf("%d ",fx[i]);puts("");
if(ok)dfs4(1),(res+=(f[1][0]+f[1][1])%mod)%=mod;
for(int j=1;j<=cnt;j++)fx[j]=-1;
}
printf("%d\n",res);
return 0;
}