zoukankan      html  css  js  c++  java
  • [ZJOI2017]仙人掌

    题面

    luogu

    分析

    如果本来就不是仙人掌那就凉啦
    特判仙人掌的话
    先dfs建树建fa
    然后对于每一条边 从它dfn大的那个点开始跳fa
    把跳过的边打标记
    显然有环的话 环上的边会被跳两次
    如果一个边被多个环覆盖 那么标记也会打两次以上

    然后显然连边的时候 选取的路径上的边都不能被环覆盖过
    所以原图就被环切成了很多树

    对于每一个树 考虑dp
    dp方式参考orz
    最后的式子
    (f[u]=prod g[v] imes h[num])
    (g[u]=f[u]+prod g[v] imes h[num-1] imes num)

    多数据注意初始化复杂度!

    #include <bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    const int N = 5e5 + 5;
    const ll P = 998244353;
    const int inf = 0x3f3f3f3f;
    struct Edge{int v, next;}edge[N << 2];
    int head[N], esize;
    inline void addedge(int x, int y){
    	edge[++esize] = (Edge){y, head[x]}, head[x] = esize;
    }
    int n, m, fa[N], dfn[N], tag[N], a[N], dfncnt; ll ans, h[N], g[N], f[N];
    inline bool rule(int x, int y){return dfn[x] < dfn[y];}
    inline void init(){
    	if(n){
    		for(int i = 1; i <= n; ++i) head[i] = -1, tag[i] = dfn[i] = fa[i] = 0;
    	}
    	else {
    		memset(head, -1, sizeof(head)), memset(tag, 0, sizeof(tag)),
        	memset(dfn, 0, sizeof(dfn)), memset(fa, 0, sizeof(fa));
    	} 
    	esize = 1, dfncnt = 0, ans = 1;
    }
    void dfs(int x, int ff){
    	dfn[x] = ++dfncnt, fa[x] = ff;
    	for(int i = head[x], vv; ~i; i = edge[i].next)
    	    if(!dfn[edge[i].v]) dfs(edge[i].v, x); 
    }
    void update(int x){
    	ll son = 0, gsum = 1; f[x] = 1, tag[x] = -1;
    	for(int i = head[x], vv; ~i; i = edge[i].next){
    		vv = edge[i].v; if(tag[vv] != 1 || vv == fa[x]) continue; 
    		update(vv);  ++son, gsum = gsum * g[vv] % P;
    	}
    	f[x] = gsum * h[son] % P, g[x] = (f[x] + gsum * h[son - 1] % P * son % P) % P;
    //	printf("%d %lld %lld
    ", x, f[x], g[x]);
    }
    int main(){
    	int T; scanf("%d", &T);
    	h[1] = h[0] = 1, h[2] = 2;
    	for(int i = 3; i <= 5e5; ++i) h[i] = (h[i - 1] + h[i - 2] * (i - 1) % P) % P;
    	while(T--){
    	    init();
    		scanf("%d%d", &n, &m); 
    		for(int i = 1, x, y; i <= m; ++i)
    			scanf("%d%d", &x, &y), addedge(x, y), addedge(y, x);
    		dfs(1, 0); bool flag = 0;
    		for(int i = 2, uu, vv; i <= esize; i += 2){
    			uu = edge[i ^ 1].v, vv = edge[i].v;
    			if(dfn[uu] < dfn[vv]) swap(uu, vv);
    			while(uu != vv){
    				++tag[uu]; 
    				if(tag[uu] > 2) {flag = 1; break;}
    				uu = fa[uu];
    			}
    			if(flag) break;
    		}
    		if(flag){printf("0
    "); continue;}
    		for(int i = 1; i <= n; ++i) a[i] = i;
    		sort(a + 1, a + n + 1, rule);
    		for(int i = 1; i <= n; ++i) if(~tag[a[i]]){
    			update(a[i]); ans = ans * f[a[i]] % P;
    		}
    		printf("%lld
    ", ans);
    	}
    	return 0;
    }
    
  • 相关阅读:
    POJ3624 01背包入门
    51Nod 1085 01背包
    linux配置安装tengine(centos6.5 )
    nginx列出目录
    Black and white painting
    Train Problem I
    快速排序的题
    数据结构实验之求二叉树后序遍历和层次遍历
    (转)最大子序列和问题 看着貌似不错
    数据结构实验之二叉树的建立与遍历
  • 原文地址:https://www.cnblogs.com/hjmmm/p/10940559.html
Copyright © 2011-2022 走看看