【题解】彩色树 51nod 1868 虚树 树上dp
Prelude
题目在这里:ο(=•ω<=)ρ⌒☆
Solution
蒟蒻__stdcall的第一道虚树题qaq。
首先很容易发现,这个排列是假的。
我们只需要求出每对点之间的颜色数量,然后求个和,然后再乘以\((n-1)!\)再乘以\(2\)就好啦!
如何求出“每对点之间的颜色数量之和”呢?
似乎点分可以做,并且fc确实写出了点分的做法,但是有更简(ma)单(nong)的虚树做法。
我们对每种颜色分开考虑,对于每种颜色\(c\),我们考虑有多少条路径经过了颜色\(c\),然后再求和,就可以了。
注意到“有多少条路径经过颜色\(c\)”,可以转化为“总的路径条数”减去“不经过颜色\(c\)的路径条数”。
“总的路径条数”等于\(\frac{n(n-1)}{2}\)。
然后我们对所有颜色\(c\)的点建出虚树,在虚树上dp就可以求出“不经过颜色\(c\)的路径条数”了。
如何dp?
考虑去掉所有的颜色\(c\)的点,剩下了一个个连通块,那么每个连通块内部的所有路径都不经过颜色\(c\),并且跨越连通块的路径一定经过颜色\(c\)。
然后就是dp求出每个连通块的大小就可以了。
这个东西。。。应该不用再讲了叭。。。我也不知道怎么解释了,要不看代码叭QAQ。
Code
#include <cstring>
#include <algorithm>
#include <cstdio>
#include <stack>
#include <vector>
#include <cassert>
using namespace std;
typedef long long ll;
typedef vector<int>::iterator viter;
const int MAXN = 100010;
const int MOD = 1e9+7;
int _w;
int n, a[MAXN];
vector<int> col[MAXN];
namespace Tree {
	int head[MAXN], nxt[MAXN<<1], to[MAXN<<1], m;
	void init() {
		m = 0;
		memset(head, -1, sizeof head);
	}
	void adde( int u, int v ) {
		to[m] = v, nxt[m] = head[u], head[u] = m++;
		to[m] = u, nxt[m] = head[v], head[v] = m++;
	}
}
namespace DFS {
	int dfn[MAXN], dfnc, top[MAXN], son[MAXN], pa[MAXN], dep[MAXN], sz[MAXN];
	void dfs1( int u, int fa, int d ) {
		using namespace Tree;
		sz[u] = 1, dep[u] = d, pa[u] = fa;
		for( int i = head[u]; ~i; i = nxt[i] ) {
			int v = to[i];
			if( v == fa ) continue;
			dfs1(v, u, d+1);
			sz[u] += sz[v];
			if( sz[v] > sz[son[u]] ) son[u] = v;
		}
	}
	void dfs2( int u, int tp ) {
		using namespace Tree;
		dfn[u] = ++dfnc, top[u] = tp;
		if( son[u] ) dfs2( son[u], tp );
		for( int i = head[u]; ~i; i = nxt[i] ) {
			int v = to[i];
			if( v == pa[u] || v == son[u] ) continue;
			dfs2(v, v);
		}
	}
	void solve() {
		dfs1(1, 0, 1);
		dfs2(1, 1);
	}
	int lca( int u, int v ) {
		while( top[u] != top[v] ) {
			if( dep[top[u]] < dep[top[v]] )
				swap(u, v);
			u = pa[top[u]];
		}
		return dep[u] < dep[v] ? u : v;
	}
	int findson( int u, int v ) {
		while( top[u] != top[v] && pa[top[v]] != u )
			v = pa[top[v]];
		if( top[u] == top[v] ) return son[u];
		else return top[v];
	}
}
int cnt[MAXN];
void prelude() {
	using namespace Tree;
	using DFS::sz;
	using DFS::pa;
	for( int u = 1; u <= n; ++u )
		for( int i = head[u]; ~i; i = nxt[i] ) {
			int v = to[i];
			if( v == pa[u] ) continue;
			cnt[u] = int((cnt[u] + (ll)sz[v] * (sz[v]-1) / 2 % MOD) % MOD);
		}
}
int vistm[MAXN];
stack<int> stk;
bool cmp_dfn( int i, int j ) {
	using DFS::dfn;
	return dfn[i] < dfn[j];
}
void vt_adde( int u, int v, int id ) {
	if( vistm[u] != id ) {
		vistm[u] = id;
		Tree::head[u] = -1;
	}
	if( vistm[v] != id ) {
		vistm[v] = id;
		Tree::head[v] = -1;
	}
	Tree::adde(u, v);
}
int build( vector<int> &vec, int id ) {
	using DFS::dep;
	Tree::m = 0;
	sort( vec.begin(), vec.end(), cmp_dfn );
	for( viter it = vec.begin(); it != vec.end(); ++it ) {
		int u = *it;
		if( stk.empty() ) {
			stk.push(u);
		} else {
			int lca = DFS::lca(u, stk.top());
			while( !stk.empty() && DFS::dep[stk.top()] > dep[lca] ) {
				int v = stk.top(); stk.pop();
				if( stk.empty() || DFS::dep[stk.top()] < dep[lca] ) {
					vt_adde(v, lca, id);
				} else {
					vt_adde(v, stk.top(), id);
				}
			}
			if( stk.empty() || stk.top() != lca )
				stk.push(lca);
			stk.push(u);
		}
	}
	while( !stk.empty() ) {
		int u = stk.top(); stk.pop();
		if( stk.empty() ) return u;
		vt_adde(u, stk.top(), id);
	}
	return assert(0), 0;
}
int vt_ans, f[MAXN];
void vt_dfs( int u, int fa, int c ) {
	using namespace Tree;
	using DFS::sz;
	for( int i = head[u]; ~i; i = nxt[i] ) {
		int v = to[i];
		if( v == fa ) continue;
		vt_dfs(v, u, c);
	}
	if( a[u] == c ) {
		f[u] = 0;
		vt_ans = (vt_ans + cnt[u]) % MOD;
		for( int i = head[u]; ~i; i = nxt[i] ) {
			int v = to[i];
			if( v == fa ) continue;
			int son = DFS::findson(u, v);
			vt_ans = int((vt_ans - (ll)sz[son] * (sz[son]-1) / 2 % MOD + MOD) % MOD);
			int tmp = sz[son] - sz[v] + f[v];
			vt_ans = int((vt_ans + (ll)tmp * (tmp-1) / 2 % MOD) % MOD);
		}
	} else {
		f[u] = sz[u];
		for( int i = head[u]; ~i; i = nxt[i] ) {
			int v = to[i];
			if( v == fa ) continue;
			f[u] = f[u] - sz[v] + f[v];
		}
	}
}
int calc( int rt, int c ) {
	using DFS::sz;
	vt_ans = 0;
	vt_dfs(rt, 0, c);
	int tmp = n - sz[rt] + f[rt];
	vt_ans = int((vt_ans + (ll)tmp * (tmp-1) / 2 % MOD) % MOD);
	return vt_ans;
}
int solve( int c ) {
	if( col[c].empty() ) return 0;
	int rt = build( col[c], c );
	if( vistm[rt] != c ) {
		vistm[rt] = c;
		Tree::head[rt] = -1;
	}
	// printf( "rt[%d] = %d\n", c, rt );
	int ans = calc(rt, c);
	ans = int(((ll)n*(n-1)/2 % MOD - ans + MOD) % MOD);
	return ans;
}
int main() {
	_w = scanf( "%d", &n );
	for( int i = 1; i <= n; ++i ) {
		_w = scanf( "%d", a+i );
		col[a[i]].push_back(i);
	}
	Tree::init();
	for( int i = 0; i < n-1; ++i ) {
		int u, v;
		_w = scanf( "%d%d", &u, &v );
		Tree::adde(u, v);
	}
	DFS::solve(), prelude();
	int ans = 0;
	for( int i = 1; i <= n; ++i ) {
		int tmp = solve(i);
		ans = (ans + tmp) % MOD;
		// printf( "path[%d] = %d\n", i, tmp );
	}
	// printf( "path = %d\n", ans );
	for( int i = 2; i <= n-1; ++i )
		ans = int((ll)ans * i % MOD);
	ans = ans * 2 % MOD;
	printf( "%d\n", ans );
	return 0;
}
【题解】彩色树 51nod 1868 虚树 树上dp的更多相关文章
- 虚树总结&题单&简要题解
		简介 虚树,即剔除所有无关结点,只保留询问点和询问点的相关结点(两两之间的LCA),建一棵新树,这棵新树就是虚树.通过虚树,可以有效的减小询问(甚至修改)的复杂度.设询问点的个数是\(k\),那么建虚 ... 
- Codechef Sad Pairs——圆方树+虚树+树上差分
		SADPAIRS 删点不连通,点双,圆方树 非割点:没有影响 割点:子树DP一下 有不同颜色,所以建立虚树 在圆方树上dfs时候 如果当前点是割点 1.统计当前颜色虚树上的不连通点对,树形DP即可 2 ... 
- BZOJ 3572: [Hnoi2014]世界树 [虚树 DP 倍增]
		传送门 题意: 一棵树,多次询问,给出$m$个点,求有几个点到给定点最近 写了一晚上... 当然要建虚树了,但是怎么$DP$啊 大爷题解传送门 我们先求出到虚树上某个点最近的关键点 然后枚举所有的边$ ... 
- 2019.4.24 一题(CF 809E)——推式子+虚树
		题目:http://codeforces.com/contest/809/problem/E 
- 虚树------sdoi2011<消耗战>
		卡着时间过得,大概是因为全用了ll,时间涨了一倍吧?? 懒得改了,第一道虚树还是思路比较重要 下面这段文字是复制来的: 给出一棵树. 每次询问选择一些点,求一些东西.这些东西的特点是,许多未选择的点可 ... 
- bzoj 3611: [Heoi2014]大工程 虚树
		题目: 国家有一个大工程,要给一个非常大的交通网络里建一些新的通道. 我们这个国家位置非常特殊,可以看成是一个单位边权的树,城市位于顶点上. 在 2 个国家 a,b 之间建一条新通道需要的代价为树上 ... 
- bzoj 3572 [Hnoi2014]世界树(虚树+DP)
		3572: [Hnoi2014]世界树 Time Limit: 20 Sec Memory Limit: 512 MBSubmit: 645 Solved: 362[Submit][Status] ... 
- [DP优化方法之虚树]
		首先我们看一篇文章 转自xyz: 给出一棵树. 每次询问选择一些点,求一些东西.这些东西的特点是,许多未选择的点可以通过某种方式剔除而不影响最终结果. 于是就有了建虚树这个技巧..... 我们可以用l ... 
- BZOJ 3879: SvT [虚树 后缀树]
		传送门 题意: 多次询问,给出一些后缀,求两两之间$LCP$之和 哈哈哈哈哈哈哈竟然$1A$了,刚才还在想如果写不好这道题下节数学就不上了,看来是上天让我上数学课啊 $Suffix\ Virtual\ ... 
随机推荐
- Laxcus大数据操作系统2.0(5)- 第二章 数据组织
			第二章 数据组织 在数据的组织结构设计上,Laxcus严格遵循数据和数据描述分离的原则,这个理念与关系数据库完全一致.在此基础上,为了保证大规模数据存取和计算的需要,我们设计了大量新的数据处理技术.同 ... 
- ntp时钟服务器配置
			集群中时间不同步有可能会让大数据的应用程序运行混乱,造成不可预知的问题,比如Hbase,当时间差别过大时就会挂掉,所以在大数据集群中,ntp服务,应该作为一种基础的服务,以下在演示在CentOS 7. ... 
- python 标准日志模块loging 及日志系统实例
			本文出处:https://www.cnblogs.com/goodhacker/p/3355660.html#undefined python的标准库里的日志系统从Python2.3开始支持.只要im ... 
- Cube Stacking P0J 1988(加权并查集)
			Description Farmer John and Betsy are playing a game with N (1 <= N <= 30,000)identical cubes ... 
- DCOM初步窥探二
			1.COM进程透明性表现在“组件对象和客户程序可以拥有各自的空间,也可以共享同一个进程空间”. COM负责把客户的调用正确传到组件对象中,并保证参数传递的正确性. 组件对象和客户代码不必考虑调用传递的 ... 
- ansible的介绍和一些基本模块介绍
			必须保证ansible工作站与各个node实现无密码ssh登入 ①:192.168.1.100 - 在你本地的工作站或服务器上安装 Ansible. ②:文件服务器1到代理服务器3 - 使用 19 ... 
- BOM对象属性定时器的调用
			使count中的内容,自动切换 <body> <h1 id="count"></h1> </body> //获取count var ... 
- IT行业大学生就业分析报告感想
			现如今的高校毕业生每年都在增长,就业压力只增不减,人才市场挤满了人 学生们普遍的表现出就业难的情况,并且适合自己的工作也难找 从报告中也容易看出IT行业很吸引人,也是人数最多的,因此机会也就变少了 在 ... 
- Eureka服务注册过程
			上篇博客<SpringCloud--Eureka服务注册和发现>介绍了Eureka的基本功能,这篇我们来聊聊eureka是如何实现的. 上图是eureka的架构图,Eureka分为Serv ... 
- C 语言疑难杂症 [转:http://blog.chinaunix.net/uid-20688544-id-1894880.html]
			无聊在网上找了些C语言的东东练一下手,竟然发现其实还有好多细节之前,没注意到,该好好复习一下先. 解决掉的问题先不发出来,把疑问的先做个笔记,过几天解决了就回来修改补上. #include < ... 
