zoukankan      html  css  js  c++  java
  • [HNOI/AHOI2018]毒瘤

    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;
    }
    
  • 相关阅读:
    一些小题
    文件操作_菜单<代码>
    文件操作
    linux基础学习
    列表,元组,字典
    系统集成项目管理工程师高频考点(第六章)
    系统集成项目管理工程师高频考点(第五章)
    系统集成项目管理工程师高频考点(第四章)
    系统集成项目管理工程师高频考点(第三章)
    信息系统项目管理师高频考点(第一章)
  • 原文地址:https://www.cnblogs.com/Troverld/p/14621490.html
Copyright © 2011-2022 走看看