题目的意思是:给定一个点带颜色的树,两点之间的距离定义为路径上不同颜色的个数。求所有点对间的距离和。

做法有点分治,还有传说中的虚树DP,树上差分。

点分治法:

  考虑每个点的贡献,可以发现一个点的子树大小就是这个点的贡献。那么,对于同一个根的另一个子树的一个点x,去掉x到根结点对应颜色的贡献,再加上x到根结点上的颜色的种类数目,就是这个x点的答案。我们具体做的时候,是先不考虑根结点的,根结点对x点的贡献单独算。

  

#include <algorithm>
#include <iterator>
#include <iostream>
#include <cstring>
#include <cstdlib>
#include <iomanip>
#include <bitset>
#include <cctype>
#include <cstdio>
#include <string>
#include <vector>
#include <stack>
#include <cmath>
#include <queue>
#include <list>
#include <map>
#include <set>
#include <cassert> /* ⊂_ヽ
  \\ Λ_Λ 来了老弟
   \('ㅅ')
    > ⌒ヽ
   /   へ\
   /  / \\
   レ ノ   ヽ_つ
  / /
  / /|
 ( (ヽ
 | |、\
 | 丿 \ ⌒)
 | |  ) /
'ノ )  Lノ */ using namespace std;
#define lson (l , mid , rt << 1)
#define rson (mid + 1 , r , rt << 1 | 1)
#define debug(x) cerr << #x << " = " << x << "\n";
#define pb push_back
#define pq priority_queue typedef long long ll;
typedef unsigned long long ull;
//typedef __int128 bll;
typedef pair<ll ,ll > pll;
typedef pair<int ,int > pii;
typedef pair<int,pii> p3; //priority_queue<int> q;//这是一个大根堆q
//priority_queue<int,vector<int>,greater<int> >q;//这是一个小根堆q
#define fi first
#define se second
//#define endl '\n' #define boost ios::sync_with_stdio(false);cin.tie(0)
#define rep(a, b, c) for(int a = (b); a <= (c); ++ a)
#define max3(a,b,c) max(max(a,b), c);
#define min3(a,b,c) min(min(a,b), c); const ll oo = 1ll<<;
const ll mos = 0x7FFFFFFF; //
const ll nmos = 0x80000000; //-2147483648
const int inf = 0x3f3f3f3f;
const ll inff = 0x3f3f3f3f3f3f3f3f; //
const int mod = 1e9+;
const double esp = 1e-;
const double PI=acos(-1.0);
const double PHI=0.61803399; //黄金分割点
const double tPHI=0.38196601; template<typename T>
inline T read(T&x){
x=;int f=;char ch=getchar();
while (ch<''||ch>'') f|=(ch=='-'),ch=getchar();
while (ch>=''&&ch<='') x=x*+ch-'',ch=getchar();
return x=f?-x:x;
} inline void cmax(int &x,int y){if(x<y)x=y;}
inline void cmax(ll &x,ll y){if(x<y)x=y;}
inline void cmin(int &x,int y){if(x>y)x=y;}
inline void cmin(ll &x,ll y){if(x>y)x=y;} /*-----------------------showtime----------------------*/
const int maxn = 2e5+;
int col[maxn];
vector<int>mp[maxn];
ll ans = , sumcol = ;
int sz[maxn],wt[maxn], root, curn;
int vis[maxn];
void findRoot(int u, int fa) {
sz[u] = ;wt[u] = ;
for(int i=; i<mp[u].size(); i++) {
int v = mp[u][i];
if(v == fa || vis[v]) continue;
findRoot(v, u);
sz[u] += sz[v];
wt[u] = max(sz[v], wt[u]);
}
wt[u] = max(wt[u], curn - sz[u]);
if(wt[u] <= wt[root]) root = u;
}
// map<int, int> pp;
ll pp[maxn];
int youmeiyou[maxn];
int ss;
void gao(int u, int fa, vector<pii>& vv, int cnt, ll sumfa, ll sum) {
ll res = ;
if(youmeiyou[col[u]] == )
vv.pb(pii(col[u], sz[u])), cnt++, res += pp[col[u]]; youmeiyou[col[u]]++;
ans += sumcol - sumfa - res + 1ll * cnt * sum;
//sum-color[根的颜色]+size[root]
if(youmeiyou[col[ss]] == ) ans += sum - pp[col[ss]];
for(int i=; i<mp[u].size(); i++) {
int v = mp[u][i];
if(fa == v || vis[v]) continue;
gao(v, u, vv, cnt, sumfa + res, sum);
}
youmeiyou[col[u]] --;
} void solve(int u) {
vis[u] = ;
findRoot(u, -);
ll sum = ;
sumcol = ;
queue<int>needclear;
needclear.push(col[u]);
for(int i=; i<mp[u].size(); i++) {
int v = mp[u][i];
if(vis[v]) continue;
vector<pii>vv;
ss = u;
gao(v, -, vv, , , sum); for(int j=; j<vv.size(); j++){
int c = vv[j].fi;
if(pp[c])pp[c] += vv[j].se;
else {
pp[c] = vv[j].se;
needclear.push(c);
}
sumcol += vv[j].se;
}
sum += sz[v];
} while(!needclear.empty()) {
pp[needclear.front()] = ;
needclear.pop();
}
for(int i=; i<mp[u].size(); i++) {
int v = mp[u][i];
if(!vis[v]) {
root = ; wt[] = inf; curn = sz[v];
findRoot(v, -);
solve(root);
}
}
}
int main(){
int n, cas = ;
while(~scanf("%d", &n)) {
memset(vis, , sizeof(vis));
for(int i=; i<=n; i++) scanf("%d", &col[i]);
for(int i=; i<=n; i++) mp[i].clear();
for(int i=; i<n; i++) {
int u,v;
scanf("%d%d", &u, &v);
mp[u].pb(v);
mp[v].pb(u);
} ans = ;
root = ; wt[] = inf;
curn = n;
findRoot(, -);
solve(root);
printf("Case #%d: %lld\n", ++cas, ans);
}
return ;
}
/*
6
1 2 3 1 2 3
1 2
1 3
3 4
3 5
4 6
*/

虚树 + 树上差分法:

  对于一种颜色,可以把树分割成许多联通块,同一个联通块内,这种颜色不会产生影响,所以某个点上,某个颜色的影响就是n - size,size是包含这个点的联通块的大小。

  由于有多种颜色,我们可以对每种颜色构建对应的虚树,选择这种颜色的点和这些点的直接儿子作为关键点。类似树上差分的思想,先把答案保存在每个联通块最上面的点。

  

#include <bits/stdc++.h>

using namespace std;
#define pb push_back
#define fi first
#define se second
#define debug(x) cerr<<#x << " := " << x << endl;
#define bug cerr<<"-----------------------"<<endl;
#define FOR(a, b, c) for(int a = b; a <= c; ++ a) typedef long long ll;
typedef long double ld;
typedef pair<int, int> pii;
typedef pair<ll, ll> pll;
typedef pair<pii, int>PII; template<class T> void _R(T &x) { cin >> x; }
void _R(int &x) { scanf("%d", &x); }
void _R(ll &x) { scanf("%lld", &x); }
void _R(double &x) { scanf("%lf", &x); }
void _R(char &x) { scanf(" %c", &x); }
void _R(char *x) { scanf("%s", x); }
void R() {}
template<class T, class... U> void R(T &head, U &... tail) { _R(head); R(tail...); } template<typename T>
inline T read(T&x){
x=;int f=;char ch=getchar();
while (ch<''||ch>'') f|=(ch=='-'),ch=getchar();
while (ch>=''&&ch<='') x=x*+ch-'',ch=getchar();
return x=f?-x:x;
} const ll inf = 0x3f3f3f3f3f3f3f3f; const int mod = 1e9+; /**********showtime************/
const int maxn = 2e5+;
int col[maxn],vis[maxn];
vector<int>mp[maxn],xu_mp[maxn];
vector<int>node[maxn],xu;
int sz[maxn], dfn[maxn], dp[maxn], tim;
int fa[maxn][];
ll fen[maxn],ans; void dfs(int u, int o) {
sz[u] = ; dfn[u] = ++tim;
fa[u][] = o;
dp[u] = dp[o] + ;
for(int i=; i<; i++)
fa[u][i] = fa[fa[u][i-]][i-];
for(int v : mp[u]) {
if(v == o) continue;
dfs(v, u);
sz[u] += sz[v];
}
} int lca(int u, int v) {
if(dp[u] < dp[v]) swap(u, v); for(int i=; i>=; i--) {
if(dp[fa[u][i]] >= dp[v])
u = fa[u][i];
}
if(u == v) return u; for(int i=; i>=; i--) {
if(fa[u][i] != fa[v][i])
u = fa[u][i], v = fa[v][i];
}
return fa[u][];
} bool cmp(int x, int y) {
return dfn[x] < dfn[y];
}
int used[maxn];
int nsz[maxn];
int curcol;
int n;
int cdp[maxn];
//求虚树上每个联通块的大小
void gaoNewSz(int u, int o) {
ll s = ;
cdp[u] = ;
for(int v : xu_mp[u]) {
if(v == o) continue;
gaoNewSz(v, u);
if(col[v] == curcol)
cdp[u] += sz[v];
else cdp[u] += cdp[v];
}
nsz[u] = n - (sz[u] - cdp[u]);
}
//建立树上的差分
void gaoSub(int u, int fa, int val) {
int w = val;
if(col[u] == curcol) {
fen[u] -= val;
}
else if(col[fa] == curcol || u == )
{
fen[u] += nsz[u];
w = nsz[u];
} for(int v : xu_mp[u]) {
if(v == fa) continue;
if(col[u] == curcol)gaoSub(v, u, );
else gaoSub(v, u, w);
}
} //建立虚树
void build(vector <int> & xu) {
sort(xu.begin(), xu.end(), cmp);
stack<int>st;
queue<int>que; for(int i=; i<xu.size(); i++) {
int u = xu[i];
if(st.size() <= ) st.push(u);
else {
int x = st.top(); st.pop();
int o = lca(x, u);
if(o == x) {
st.push(x);
st.push(u);
continue;
}
while(!st.empty()) {
int y = st.top(); st.pop(); if(dfn[y] > dfn[o]) {
xu_mp[y].pb(x);
if(used[y] == ) used[y] = , que.push(y);
x = y;
}
else if(dfn[y] == dfn[o]) {
xu_mp[y].pb(x);
st.push(y);
if(used[y] == ) used[y] = , que.push(y);
break;
}
else {
xu_mp[o].pb(x);
st.push(y);
st.push(o);
if(used[o] == ) used[o] = , que.push(o);
break;
}
}
st.push(u);
}
}
while(st.size() > ) {
int u = st.top(); st.pop();
int v = st.top();
xu_mp[v].pb(u);
//xu_mp[u].pb(v);
// if(used[u] == 0) used[u] = 1, que.push(u);
if(used[v] == ) used[v] = , que.push(v);
}
while(!st.empty())st.pop(); gaoNewSz(, );
gaoSub(, , ); while(!que.empty()) {
int u = que.front();
xu_mp[u].clear();
used[u] = ;
que.pop();
}
} //树上差分,最后的更新
void pushdown(int u, int fa, ll val) {
ans += fen[u] + val + n;
val += fen[u];
for(int v : mp[u]) {
if(v == fa) continue;
pushdown(v, u, val);
}
} int main(){
int cas = ;
while(~scanf("%d", &n)){
ans = ;tim = ;
for(int i=; i<=n; i++){
mp[i].clear();
fen[i] = ;
vis[i] = ;
dp[i] = ;
node[i].clear();
}
for(int i=; i<=n; i++) {
read(col[i]);
vis[col[i]] = ;
node[col[i]].pb(i);
}
for(int i=; i<n; i++) {
int u,v;
read(u); read(v);
mp[u].pb(v);
mp[v].pb(u);
} dfs(, ); for(int i=; i<maxn; i++) {
if(vis[i]) {
xu.clear();
if(col[] != i) xu.pb();
for(int v : node[i]) {
xu.pb(v);
for(int k : mp[v]) {
if(col[k] != i && dp[k] > dp[v])
xu.pb(k);
}
}
curcol = i;
build(xu);
}
}
pushdown(, , );
printf("Case #%d: %lld\n", ++cas, (ans - n )/ );
}
return ;
}

附上虚树建立的网上流行模板

void insert(int x) {
if(top == ) {s[++top] = x; return ;}
int lca = LCA(x, s[top]);
if(lca == s[top]){ s[++top] = x;return ;}
while(top > && dfn[s[top - ]] >= dfn[lca]) add_edge(s[top - ], s[top]), top--;
if(lca != s[top]) add_edge(lca, s[top]), s[top] = lca;//
s[++top] = x;
}

【HDU6035】 Colorful Tree的更多相关文章

  1. 【hdu6035】 Colorful Tree dfs序

    题目传送门:http://acm.hdu.edu.cn/showproblem.php?pid=6035 题目大意:给你一棵树,树上每个节点都有一个颜色. 现在定义两点间的距离为两点最短路径上颜色集合 ...

  2. 【BZOJ】2631: tree LCT

    [题意]给定n个点的树,每个点初始权值为1,m次操作:1.x到y的点加值,2.断一条边并连一条边,保证仍是树,3.x到y的点乘值,4.x到y的点权值和取模.n,m<=10^5. [算法]Link ...

  3. 【BZOJ2212】[Poi2011]Tree Rotations 线段树合并

    [BZOJ2212][Poi2011]Tree Rotations Description Byteasar the gardener is growing a rare tree called Ro ...

  4. 【题解】Digit Tree

    [题解]Digit Tree CodeForces - 716E 呵呵以为是数据结构题然后是淀粉质还行... 题目就是给你一颗有边权的树,问你有多少路径,把路径上的数字顺次写出来,是\(m\)的倍数. ...

  5. 【题解】[P4178 Tree]

    [题解]P4178 Tree 一道点分治模板好题 不知道是不是我见到的题目太少了,为什么这种题目都是暴力开值域的桶QAQ?? 问点对,考虑点分治吧.直接用值域树状数组开下来,统计的时候直接往树状数组里 ...

  6. 【总结】Link-Cut Tree

    这是一篇关于LCT的总结 加删边的好朋友--Link Cut Tree Link-Cut Tree,LCT的全称 可以说是从树剖引出的问题 树剖可以解决静态的修改或查询树的链上信息:那如果图会不断改变 ...

  7. 【BZOJ】1468: Tree(POJ1741) 点分治

    [题意]给定带边权树,求两点距离<=k的点对数.n<=40000. [算法]点分治 [题解]对于一个区域,选择其重心x作为根,则划分出来的每棵子树都是子区域,可以证明至多划分log n次( ...

  8. 【Manacher】Colorful String

    The value of a string s is equal to the number of different letters which appear in this string. You ...

  9. 【题解】【BT】【Leetcode】Binary Tree Preorder/Inorder/Postorder (Iterative Solution)

    [Inorder Traversal] Given a binary tree, return the inorder traversal of its nodes' values. For exam ...

随机推荐

  1. 主机cpu突然飙高,如何快速排查问题

    [问题发现] 使用zabbix软件监控服务器时发现cpu突然异常,在业务主机上使用top命令查看系统的整体运行情况,使用top命令后发现mysqld占用CPU特别高,初步判断可能是mysqld出现问题 ...

  2. 【Java】设置 JPanel 宽度

    panel.setSize(200, 300); //该方法无效 panel.setPreferredSize(new Dimension(800, 0)); //使用该方法 参考链接: http:/ ...

  3. Iterator-Java

    在Java中,Iterator的作用就是为了方便处理集合中的元素.例如获取和删除集合中的元素. 在JDK8,Iterator接口提供了如下方法: 迭代器Iterator最基本的两个方法是next()和 ...

  4. HashMap常见面试题整理

    花了三天时间来仔细阅读hashMap的源码,期间补了下不少数据结构的知识,刷了不少相关的面试题并进行了整理 1.谈一下HashMap的特性? 1.HashMap存储键值对实现快速存取,允许为null. ...

  5. vmware15pro安装ubuntu18.10时出现显示不全问题

    如果这个时候用网上的ALT+左键拖拽根本没有效果 所以这里提供另外一种方式 就是正常安装的时候发现分区部分显示不全 此时点击右上角的橙色小×:询问是否退出 我们点击退出:之后就会来到试用界面 到了这里 ...

  6. codeforces679A_Bear and Prime 100 交互题

    传送门 第一道交互题 题意: 电脑事先想好了一个数[,] 你会每次问电脑一个数是否是它想的那个数的因数 电脑会告诉你yes或no 至多询问20次 最后要输出它想的数是质数还是合数 思路: 枚举< ...

  7. 【Java例题】1.3给朋友的贺卡

    3.对“Hello World”程序进行改造, 能够显示一张发给朋友的贺卡.格式如下: ****************************** 张三,你好! 祝你学习愉快! 你的好朋友:李四 2 ...

  8. Windows 下安装 Python + Django

    Django是Python的一个Web开发框架,以下是介绍的是windows下的安装步骤, 作者的环境是Win10 ,Windows Server 也是一样的 以下是作者整理的步骤,也可以参考官方教程 ...

  9. Promise 学习心得

    当了这么久码农到今天没事才开始去深究 Promise 这个对象 什么是 Promise, Promise 有什么用? 在写代码的时候多多少少都有遇见过地狱式的回调 代码看起来没问题就是有点乱,Prom ...

  10. 关于 IntelliJ 的 IDEA PyCharm 等更新 2019.2 后中文乱码 的解决方案

    关于IntelliJ 的2019.2 更新后的中文乱码解决方案 设置 备用字体 file -> Setting -> Editor ->Font 由于编程常用英文首选字体font默认 ...