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

做法有点分治,还有传说中的虚树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. Android:JNI与NDK(二)交叉编译与动态库,静态库

    欢迎关注公众号,第一时间获取最新文章: 本篇目录 一.前言 本篇主要以window开发环境为背景介绍一下NDK开发中需要掌握的交叉编译等基础知识,选window系统主要是照顾大多数读者,mac ,li ...

  2. 在WPF中嵌入WebBrowser可视化页面

    无论是哪种C/S技术,涉及数据可视化就非常的累赘了,当然大神也一定有,只不过面向大多数人,还是通过网页来实现,有的时候不想把这两个功能分开,一般会是客户的原因,所以我们打算在WPF中嵌入WebBrow ...

  3. 【Intellij】导入 jar 包

    选中工具栏上"File"--->"Project Structure"--->选择“Libraries”--->点击“+”--->选择自 ...

  4. 如何创建Github创库

    重点:利用Markdown语言写简单的日常使用的文本 基础写作和语法格式 本篇文章的内容来源于Github的基础写作帮助.如果在观看时有什么问题,可以直接查阅源文件.另外需要说明的是Git对Markd ...

  5. vue-cli中的跨域之proxytable

    为什么会有跨域? 浏览器有一个叫做同源策略的东西.同源策略限制了从同一个源加载的文档或脚本如何与来自另一个源的资源进行交互.这是一个用于隔离潜在恶意文件的重要安全机制. 同源策略规定了如果两个页面的协 ...

  6. collection介绍

    1.collection介绍 在mongodb中,collection相当于关系型数据库的表,但并不需提前创建,更不需要预先定义字段 db.collect1.save({username:'mayj' ...

  7. String关键字

    关于String和new String()见我写的前一篇博客 String和new String()的区别 1.String的"+"运算 a.String str = " ...

  8. React 练习项目,仿简书博客写作平台

    Introduction 技术栈:react + redux + react-router + express + Nginx 练习点: redux 连接 react-router 路由跳转 scss ...

  9. Apex 获取真正的IP地址

    代码如下 declare l_ip varchar2(15); begin if OWA_UTIL.GET_CGI_ENV('X-FORWARDED-FOR') is not null then l_ ...

  10. TDH 安装 TDH-Client

    1. TDH-Client 下载 (下载分享:链接:https://pan.baidu.com/s/1ZmP4BUCiuRypCtsoAuvKRA     提取码:xsbl ) tar -vxf td ...