Colorful Tree
Time Limit: 6000/3000 MS (Java/Others) Memory Limit: 131072/131072 K (Java/Others)
Total Submission(s): 1651 Accepted Submission(s): 675
The path between each two different nodes is unique, of which we define the value as the number of different colors appearing in it.
Calculate the sum of values of all paths on the tree that has n(n−1)2 paths in total.
For each test case, the first line contains one positive integers n, indicating the number of node. (2≤n≤200000)
Next line contains n integers where the i-th integer represents ci, the color of node i. (1≤ci≤n)
Each of the next n−1 lines contains two positive integers x,y (1≤x,y≤n,x≠y), meaning an edge between node x and node y.
It is guaranteed that these edges form a tree.
问题概述:
一棵树上有n个节点,每个节点都有颜色(颜色标号为1到n),因为有n个点,所以有n*(n-1)/2条不同的路径,现规定某条路径的权值为路径上所有不同颜色的个数(包括端点),求出所有路径权值之和
可以先看另一道题:
一棵树上有n个节点,每个节点要不是黄色的,要不是白色的,因为有n个点,所以有n*(n-1)/2条不同的路径,现规定如果这个路径上有黄色的点(端点也算)那么这条路径的权值为1,否则为0,求所有路径权值之和
思路:
很显然答案就是n*(n-1)/2减去不经过黄点的路径之和
然后将所有的连在一起的白点当成一块(这个块要竟可能大,也就是说块的外围一定全是黄点),对于每一块求出它的大小siz,那么这一块中权值为0的边个数就为siz*(siz-1),最后全部加在一起即可
主要问题:如何求出每一块的siz?
②对于每一个父亲为黄色的节点x,找出以x为根子树中所有其他黄色的节点(y1, y2, y3…),其中对于所有的yi与x中间的路径上都不能存在黄点,否则不算,则节点x所在那一块当前siz值就为siz[x]-∑siz[yi],那么怎么找?求出dfs序就好办了,看下面的①处
L[x]:节点x是第L[x]个遍历的不同的点
R[x]:在节点x的所有儿子已经遍历结束回溯时已经遍历过了R[x]个不同的点
①性质:以节点x为根的子树中所有节点的L[]都大于L[x],R[]都小于等于R[x]
DFS:遍历顺序为从左到右
那么原题该怎么写?仔细看看就会发现其实这两道题一模一样,只不过第二题只有1种颜色,原题有n种颜色
把原题的每种颜色单独考虑就是第二题了,最后答案全部加在一起即可!
#include<stdio.h>
#include<algorithm>
#include<vector>
using namespace std;
#define LL long long
vector<int> C[200005], G[200005];
int cnt, L[200005], R[200005], fa[200005], siz[200005];
void Sech(int u, int p)
{
int v, i;
fa[u] = p, siz[u] = 1;
L[u] = ++cnt;
for(i=0;i<G[u].size();i++)
{
v = G[u][i];
if(v==p)
continue;
Sech(v, u);
siz[u] += siz[v];
}
R[u] = cnt;
}
bool comp(int x, int y)
{
if(L[x]<L[y])
return 1;
return 0;
}
int main(void)
{
LL ans;
int n, i, j, k, x, y, size, now, cas = 0;
while(scanf("%d", &n)!=EOF)
{
cnt = 0;
for(i=0;i<=n;i++)
G[i].clear(), C[i].clear();
for(i=1;i<=n;i++)
{
scanf("%d", &x);
C[x].push_back(i);
}
for(i=1;i<=n-1;i++)
{
scanf("%d%d", &x, &y);
G[x].push_back(y);
G[y].push_back(x);
}
G[0].push_back(1);
Sech(1, 0);
ans = (LL)n*n*(n-1)/2;
for(i=1;i<=n;i++)
{
if(C[i].empty()) //如果不存在第i种颜色
{
ans -= (LL)n*(n-1)/2;
continue;
}
C[i].push_back(0);
sort(C[i].begin(), C[i].end(), comp); //按照dtime排序
for(j=0;j<C[i].size();j++)
{
x = C[i][j];
for(k=0;k<G[x].size();k++) //步骤②
{
y = G[x][k];
if(y==fa[x])
continue;
size = siz[y];
now = L[y];
while(1) //遍历子树中所有同色节点(中间路径上不能存在该颜色)
{
L[n+1] = now;
auto it = lower_bound(C[i].begin(), C[i].end(), n+1, comp);
if(it==C[i].end() || R[*it]>R[y]) //遍历完毕
break;
size -= siz[*it];
now = R[*it]+1;
}
ans -= (LL)size*(size-1)/2;
}
}
}
printf("Case #%d: %lld\n", ++cas, ans);
}
return 0;
}