容斥原理+并查集。。第一次见这样的题,涨姿势了,不看题解想不出。
看了题解后才发现很简单。
题解:
http://blog.csdn.net/crybymyself/article/details/68062911
http://www.cnblogs.com/Stomach-ache/p/3931848.html
一个讲的细,用dfs求的连通分量。
一个讲的粗略,用的并查集。。
思路:首先考虑所有的黑边是没用的,所以用并查集构造出一个个全是黑边的连通块。如果有满足要求的3个点a,b,c,若把a和b之间的红色边去掉,a和b将不连通。同理,如果把a到b,b到c,c到a中的红色边都去掉,那么a,b,c将属于不同的连通分量。假设有k个连通块,每个连通块有cnt[i]个点,这就转化成从k个连通块中选择a,b,c三个点且两两不再同一连通块中的方案数。
res = 所有点选三个点的方案数-三个点在同一个块中的方案数-两个点在同一个连通块另一个点在别的块方案数。。
using System;
using System.IO;
namespace timeless
{
class Program
{
private static long MAXN = 50010;
private static long[] f;
private static long[] size;
private static long[] cnt;
private static long mod = (long)1e9 + 7;
static void Main(string[] args)
{
StreamReader sr = new StreamReader(Console.OpenStandardInput());
StreamWriter sw = new StreamWriter(Console.OpenStandardOutput());
f = new long[MAXN];
size = new long[MAXN];
cnt = new long[MAXN];
long n = Convert.ToInt64(sr.ReadLine());
f[0] = size[0] = cnt[0] = 0;
for (int i = 1; i <= n; ++i)
{
f[i] = i;
size[i] = 1;
cnt[i] = 0;
}
string[] str;
long s,e;
char ch;
for (int i = 0; i < n-1; ++i)
{
str = sr.ReadLine().Split(' ');
s = Convert.ToInt64(str[0]);
e = Convert.ToInt64(str[1]);
ch = str[2].ToCharArray()[0];
if (ch == 'b')
merge(s, e);
}
long sum = 0;
long cntLen = 0;
long res = 0;
for (int i = 1; i <= n; ++i)
{
if (f[i] == i)
{
cnt[cntLen++] = size[i];
sum += size[i];
//在一个连通块里选择三个点的情况
res -= calC(size[i]);
}
}
res += calC(sum);//在所有点中选三个点的情况
for (int i = 0; i < cntLen; ++i)
{
//在一个块里选两个点的情况
res -= cnt[i] * (cnt[i]-1) / 2 * (sum - cnt[i]);
}
res = res % mod;
sw.WriteLine(res);
sw.Flush();
sw.Close();
sr.Close();
}
public static long calC(long x)
{
return x * (x - 1) * (x - 2) / 6;
}
public static void merge(long x, long y)
{
x = getf(x);
y = getf(y);
if (x != y)
{
size[x] += size[y];
f[y] = x;
}
}
public static long getf(long x)
{
if (x == f[x])
return x;
f[x] = getf(f[x]);
return f[x];
}
}
}