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的更多相关文章

  1. 虚树总结&题单&简要题解

    简介 虚树,即剔除所有无关结点,只保留询问点和询问点的相关结点(两两之间的LCA),建一棵新树,这棵新树就是虚树.通过虚树,可以有效的减小询问(甚至修改)的复杂度.设询问点的个数是\(k\),那么建虚 ...

  2. Codechef Sad Pairs——圆方树+虚树+树上差分

    SADPAIRS 删点不连通,点双,圆方树 非割点:没有影响 割点:子树DP一下 有不同颜色,所以建立虚树 在圆方树上dfs时候 如果当前点是割点 1.统计当前颜色虚树上的不连通点对,树形DP即可 2 ...

  3. BZOJ 3572: [Hnoi2014]世界树 [虚树 DP 倍增]

    传送门 题意: 一棵树,多次询问,给出$m$个点,求有几个点到给定点最近 写了一晚上... 当然要建虚树了,但是怎么$DP$啊 大爷题解传送门 我们先求出到虚树上某个点最近的关键点 然后枚举所有的边$ ...

  4. 2019.4.24 一题(CF 809E)——推式子+虚树

    题目:http://codeforces.com/contest/809/problem/E

  5. 虚树------sdoi2011<消耗战>

    卡着时间过得,大概是因为全用了ll,时间涨了一倍吧?? 懒得改了,第一道虚树还是思路比较重要 下面这段文字是复制来的: 给出一棵树. 每次询问选择一些点,求一些东西.这些东西的特点是,许多未选择的点可 ...

  6. bzoj 3611: [Heoi2014]大工程 虚树

    题目: 国家有一个大工程,要给一个非常大的交通网络里建一些新的通道. 我们这个国家位置非常特殊,可以看成是一个单位边权的树,城市位于顶点上. 在 2 个国家 a,b 之间建一条新通道需要的代价为树上 ...

  7. bzoj 3572 [Hnoi2014]世界树(虚树+DP)

    3572: [Hnoi2014]世界树 Time Limit: 20 Sec  Memory Limit: 512 MBSubmit: 645  Solved: 362[Submit][Status] ...

  8. [DP优化方法之虚树]

    首先我们看一篇文章 转自xyz: 给出一棵树. 每次询问选择一些点,求一些东西.这些东西的特点是,许多未选择的点可以通过某种方式剔除而不影响最终结果. 于是就有了建虚树这个技巧..... 我们可以用l ...

  9. BZOJ 3879: SvT [虚树 后缀树]

    传送门 题意: 多次询问,给出一些后缀,求两两之间$LCP$之和 哈哈哈哈哈哈哈竟然$1A$了,刚才还在想如果写不好这道题下节数学就不上了,看来是上天让我上数学课啊 $Suffix\ Virtual\ ...

随机推荐

  1. 关于SQL 语句常用的一些查询收藏

    create database xuesheng go use xuesheng go /*学生表*/ create table Student ( S# ,) primary key, Sname ...

  2. 17 Tips For Writing An Excellent Email Subject Line

    Out of the billions of emails that are sent every day, how can you make sure that yours stands out? ...

  3. Centos7 Ntp 时间服务器

    Centos7 Ntp 时间服务器 安装环境 [root@m02 ~]# cat /etc/redhat-release CentOS Linux release 7.4.1708 (Core) 安装 ...

  4. DB2 V9 默认帐户信息和服务启动信息

    1 dasusr1 DB2 管理服务器用户是管理DAS(Database Adminitrator Service).要完全适用db2 cc 必须启动DAS.DB2 管理服务器(DAS)响应来自 DB ...

  5. CCF——相邻数对201409-1

    问题描述 给定n个不同的整数,问这些数中有多少对整数,它们的值正好相差1. 输入格式 输入的第一行包含一个整数n,表示给定整数的个数. 第二行包含所给定的n个整数. 输出格式 输出一个整数,表示值正好 ...

  6. 模板CodeTemplate

    /** * @author:dubbo@xxxx.com * @date: ${date} ${time} * @version: V1.0 * @review: dubbo/${date} ${ti ...

  7. locust 性能测试学习 第一天

    不废话 1.安装python库 pip install locustio pip install pyzmq 2.脚本 命名为locust_test.py from locust import Htt ...

  8. node+express搭建个人网站(1)

    我的个人网站 http://yangchaojie.top/ 首先了解一下node Node.js 是一个基于 Chrome V8 引擎的 JavaScript 运行环境. Node.js 使用了一个 ...

  9. 第87天:HTML5中新选择器querySelector的使用

    一.HTML5新选择器 1.document.querySelector("selector");selector:根据CSS选择器返回第一个匹配到的元素,如果没有匹配到,则返回n ...

  10. 网页移动到一个高度后加载网页元素【getBoundingClientRect好用】

    $(window).scroll(function () { var windowH = $(window).height();//取可视窗口的高度 ).getBoundingClientRect() ...