zoukankan      html  css  js  c++  java
  • BZOJ 4013/Luogu P3240 [HNOI2015] 实验比较 (树形DP)

    题目传送门

    分析

    放一个dalao博客: xyz32768 的博客,看完再回来看本蒟蒻的口胡吧(其实嘛…不回来也行)

    • 精髓是合并的方案数的计算,至于为什么是Ci1j1large C_{i-1}^{j-1},是因为当前点必须独立成为第一部分
    • 时间复杂度的O(n3)O(n^3)也是个玄学东西。其实是因为枚举j,kj,k时上限分别是sz[u]sz[u](到目前所有子树的大小)和sz[v]sz[v](这棵子树的大小),乘起来就是相当于在uu下方中枚举不同子树内的点对。那么每一对只会在lcalca处被枚举到。因为在lcalca下方,两个点不可能一起被枚举到;而在lcalca上方,它们已经存在于同一子树了。所以这个枚举总时间复杂度是O(n2)O(n^2)。最终复杂度就为O(n3)O(n^3)
    • 注意判断环的方式有没有考虑周全。因为不能用简单的用入度为00来判断每一棵树的根节点(因为可能有环),我写的naivenaiveO(n)O(n)的判环各种姿势WAWA,见下
    • 第一发
      在这里插入图片描述
    • 第二发
      在这里插入图片描述

    • 在这里插入图片描述

    • 在这里插入图片描述
    • 。。。于是直接O(n2)O(n^2)每次清零visvis数组跑dfsdfs。能过就行。
    • CODE

    #include <bits/stdc++.h>
    using namespace std;
    const int MAXN = 105;
    const int mod = 1e9+7;
    int n, m, edgecnt;
    struct edge { int u, v; }e[MAXN];
    
    int scc[MAXN];
    int find(int x) { return scc[x] == x ? x : scc[x]=find(scc[x]); }
    
    int fir[MAXN], to[MAXN], nxt[MAXN], cnt, in[MAXN];
    inline void add(int u, int v) { to[++cnt] = v; nxt[cnt] = fir[u]; fir[u] = cnt; in[v]++; }
    
    bool vis[MAXN];
    bool check(int u) {
    	if(vis[u]) return 0;
    	vis[u] = 1;
    	for(int i = fir[u]; i; i = nxt[i])
    		if(!check(to[i])) return 0;
    	return 1;
    }
    
    int f[MAXN][MAXN], sz[MAXN], tmp[MAXN], c[MAXN][MAXN];
    void dp(int u) {
    	f[u][1] = sz[u] = 1;
    	for(int l = fir[u], v; l; l = nxt[l]) {
    		dp(v = to[l]);
    		for(int i = 1; i <= sz[u]+sz[v]; ++i) tmp[i] = 0;
    		for(int i = 1; i <= sz[u]+sz[v]; ++i)
    			for(int j = 1; j <= sz[u] && j <= i; ++j)
    				for(int k = 1; k <= sz[v] && k <= i; ++k)
    					if(k + j >= i)
    						tmp[i] = (tmp[i] + 1ll * f[u][j] * f[v][k] % mod * c[i-1][j-1] % mod * c[j-1][k-(i-j)] % mod) % mod;
    		for(int i = 1; i <= sz[u]+sz[v]; ++i) f[u][i] = tmp[i];
    		sz[u] += sz[v];
    	}
    }
    
    int main () {
    	scanf("%d%d", &n, &m);
    	for(int i = 1; i <= n; ++i) scc[i] = i;
    	char s[2];
    	for(int i = 1, x, y; i <= m; ++i) {
    		scanf("%d%s%d", &x, s, &y);
    		if(s[0] == '=') scc[find(y)]=find(x);
    		else {
    			if(s[i] == '>') swap(x, y); //emmm... unnecessary
    			e[++edgecnt] = (edge){ x, y };
    		}
    	}
    	for(int i = 1; i <= n; ++i) find(i);
    	for(int i = 1; i <= edgecnt; ++i) {
    		if(scc[e[i].u] == scc[e[i].v]) return puts("0"), 0;
    		add(scc[e[i].u], scc[e[i].v]);
    	}
    	bool flg = 1;
    	for(int i = 1; i <= n; ++i) if(scc[i] == i) {
    		memset(vis, 0, sizeof vis);
    		if(!check(i)) { flg = 0; break; } //judge the circle
    		if(!in[i]) add(0, i);
    	}
    	if(!flg) return puts("0"), 0;
    	c[0][0] = 1;
    	for(int i = 1; i <= n; ++i) {
    		c[i][0] = c[i][i] = 1;
    		for(int j = 1; j < i; ++j)
    			c[i][j] = (c[i-1][j-1] + c[i-1][j]) % mod;
    	}
    	dp(0);
    	int ans = 0;
    	for(int i = 1; i <= sz[0]; ++i)
    		ans = (ans + f[0][i]) % mod;
    	printf("%d
    ", ans);
    }
    
    • 出于良心的UpdUpd

      正确的O(n)O(n)判环方式是用inqinq数组存每个点是否在当前dfsdfs栈中,如果下一个点已经在栈中那么存在环,returnreturn时出栈。
  • 相关阅读:
    一个你爱,一个爱你,你选择哪一个
    Flask 中的 SQLAlchemy 使用教程
    Flas-SQLAchemy数据库操作使用学习笔记
    Flask-SQLAlchemy获取一个字段里去掉重复的数据
    Python 列表(list)、字典(dict)、字符串(string)常用基本操作小结
    pycharm快捷键、常用设置、包管理
    [原创]python MySQLdb在windows环境下的安装、出错问题以及解决办法
    Highcharts下载与使用_数据报表图
    Markdown入门指南-指间阁
    Sublime Text3 配置markdown插件
  • 原文地址:https://www.cnblogs.com/Orz-IE/p/12039425.html
Copyright © 2011-2022 走看看