zoukankan      html  css  js  c++  java
  • #树形dp#洛谷 3687 [ZJOI2017]仙人掌

    题目

    给定一个简单无向连通图,问有多少种加边方案使得这个图变成简单仙人掌。


    分析

    首先找到一棵生成树,考虑其它非树边所对应的树的路径上的边最多只能用一次,

    这可以用树上差分做,如果一个点到其父节点的边被用了多次就一定无解。

    否则没有被用过的部分将形成若干棵树,方案就是它们的乘积。

    等于说问用若干条路径覆盖一棵树的方案。

    (dp[x]) 表示以 (x) 为根的子树的方案数。

    只需要考虑 (x) 的邻边如何连接,子树的答案直接相乘。

    (f[n]) 表示 (n) 条边任意匹配的方案数,则

    (f[n]=f[n-2]*(n-1)+f[n-1])

    那么 (dp[x]*=f[deg[x]])


    代码

    #include <cstdio>
    #include <cctype>
    #define rr register
    using namespace std;
    const int N=500011,mod=998244353; struct node{int y,next;}e[N<<1],E[N<<1];
    int f[N],c[N],v[N],as[N],hs[N],et=1,Et=1,n,dp[N],ans,fat[N],g[N],flag,upd,Lca[N<<1];
    inline signed iut(){
    	rr int ans=0; rr char c=getchar();
    	while (!isdigit(c)) c=getchar();
    	while (isdigit(c)) ans=(ans<<3)+(ans<<1)+(c^48),c=getchar();
    	return ans;
    }
    inline void print(int ans){
    	if (ans>9) print(ans/10);
    	putchar(ans%10+48);
    }
    inline signed getf(int u){return f[u]==u?u:f[u]=getf(f[u]);}
    inline void Clear(){
    	for (rr int i=1;i<=n;++i) c[i]=as[i]=hs[i]=0;
    }
    inline void dfs1(int x,int fa){
    	f[x]=x,v[x]=upd,fat[x]=fa;
    	for (rr int i=as[x];i;i=e[i].next)
    	    if (e[i].y!=fa) dfs1(e[i].y,x),f[e[i].y]=x;
        for (rr int i=hs[x];i;i=E[i].next)
    	    if (v[E[i].y]==upd) Lca[i]=Lca[i^1]=getf(E[i].y);
    }
    inline void dfs2(int x,int fa){
    	for (rr int i=as[x];i;i=e[i].next)
    	    if (e[i].y!=fa) dfs2(e[i].y,x),c[x]+=c[e[i].y];
    }
    inline void dfs3(int x,int TOP){
    	rr int sub=0; dp[x]=1;
    	for (rr int i=as[x];i;i=e[i].next)
    	if (e[i].y!=fat[x]){
    		if (!c[e[i].y]) dfs3(e[i].y,TOP),++sub,dp[x]=1ll*dp[x]*dp[e[i].y]%mod;
    		    else dfs3(e[i].y,e[i].y);
    	}
    	if (x==TOP) ans=1ll*ans*dp[x]%mod*g[sub]%mod;
    	    else dp[x]=1ll*dp[x]*g[sub+1]%mod;
    }
    signed main(){
    	g[0]=g[1]=1;
    	for (rr int i=2;i<N;++i) g[i]=(g[i-2]*(i-1ll)+g[i-1])%mod;
    	for (rr int T=iut();T;--T,putchar(10)){
    		n=iut(),et=Et=ans=flag=1,++upd;
    		for (rr int i=1;i<=n;++i) f[i]=i;
    		for (rr int TOT=iut();TOT;--TOT){
    			rr int x=iut(),y=iut();
    			if (getf(x)!=getf(y)){
    				rr int fa=getf(x),fb=getf(y);
    				if (fa>fb) fa^=fb,fb^=fa,fa^=fb;
    				f[fa]=fb;
    				e[++et]=(node){y,as[x]},as[x]=et;
    				e[++et]=(node){x,as[y]},as[y]=et;
    			}else{
    			    E[++Et]=(node){y,hs[x]},hs[x]=Et;
    			    E[++Et]=(node){x,hs[y]},hs[y]=Et;
    			}
    		}
    		dfs1(1,0);
    		for (rr int i=2;i<=Et;i+=2)
    		    ++c[E[i].y],++c[E[i^1].y],c[Lca[i]]-=2;
    		dfs2(1,0);
    		for (rr int i=1;i<=n&&flag;++i)
    		    if (c[i]>1) putchar(48),flag=0;
    		if (!flag) {Clear(); continue;}
    		dfs3(1,1),print(ans),Clear();
    	}
    	return 0;
    }
    
  • 相关阅读:
    关于获取路径
    今天最好的生日礼物就是重新找到目标
    Fedora与Ubuntu安装g++的命令
    CMPXCHG8B 比较并交换 8 字节
    关于 WIN32_LEAN_AND_MEAN
    i386和i686
    Intrinsic function
    VC9: LINK : warning LNK4068: /MACHINE not specified; defaulting to X86
    Linux内核中的Min和Max函数
    linux重定向命令应用及语法
  • 原文地址:https://www.cnblogs.com/Spare-No-Effort/p/15424224.html
Copyright © 2011-2022 走看看