51nod 1253 Kundu and Tree

来源:互联网 发布:液晶编程器烧录器 编辑:程序博客网 时间:2024/06/13 04:54

容斥原理+并查集。。第一次见这样的题,涨姿势了,不看题解想不出。这里写图片描述
看了题解后才发现很简单。
题解:
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];        }    }}