zoukankan      html  css  js  c++  java
  • [BZOJ 1016] [JSOI2008] 最小生成树计数 【DFS】

    题目链接:BZOJ - 1016

    题目分析

    最小生成树的两个性质:

    同一个图的最小生成树,满足:

    1)同一种权值的边的个数相等

    2)用Kruscal按照从小到大,处理完某一种权值的所有边后,图的连通性相等

    这样,先做一次Kruscal求出每种权值的边的条数,再按照权值从小到大,对每种边进行 DFS, 求出这种权值的边有几种选法。

    最后根据乘法原理将各种边的选法数乘起来就可以了。

    特别注意:在DFS中为了在向下DFS之后消除决策影响,恢复f[]数组之前的状态,在DFS中调用的Find()函数不能路径压缩。 

    代码

    #include <iostream>
    #include <cstdlib>
    #include <cstring>
    #include <cmath>
    #include <cstdio>
    #include <algorithm>
    
    using namespace std;
    
    const int MaxN = 100 + 5, MaxM = 1000 + 5, Mod = 31011;
    
    int n, m, Top, Sum, Cnt;
    int f[MaxN], L[MaxM], R[MaxM], Num[MaxM];
    
    typedef long long LL;
    LL Ans;
    
    struct Edge
    {
    	int u, v, w; 
    } E[MaxM];
    
    inline bool Cmp(Edge e1, Edge e2) 
    {
    	return e1.w < e2.w;
    }
    
    inline int Find(int x, int o) 
    {
    	int i, j, k;
    	j = x; 
    	while (j != f[j]) j = f[j];
    	if (o == 1) return j;
    	i = x;
    	while (i != j) 
    	{
    		k = i;
    		i = f[i];
    		f[k] = j;
    	}
    	return j;
    }
    
    void DFS(int Type, int x, int y) 
    {
    	if (x == R[Type] + 1) 
    	{
    		if (y == Num[Type]) ++Cnt;
    		return;
    	}
    	int fx, fy;
    	fx = Find(E[x].u, 1); fy = Find(E[x].v, 1);
    	if (fx != fy)
    	{
    		f[fx] = fy;
    		DFS(Type, x + 1, y + 1);
    		f[fx] = fx;
    	}
    	DFS(Type, x + 1, y);
    }
    
    int main() 
    {
    	scanf("%d%d", &n, &m);
    	for (int i = 1; i <= m; ++i)
    		scanf("%d%d%d", &E[i].u, &E[i].v, &E[i].w);
    	sort(E + 1, E + m + 1, Cmp);
    	Top = 0; Sum = 0;
    	int fx, fy;
    	for (int i = 1; i <= n; ++i) f[i] = i;
    	for (int i = 1; i <= m; ++i) 
    	{
    		if (i == 1 || E[i].w != E[i - 1].w) L[++Top] = i;
    		R[Top] = i;
    		fx = Find(E[i].u, 0); fy = Find(E[i].v, 0);
    		if (fx != fy) 
    		{
    			f[fx] = fy;
    			++Num[Top];
    			++Sum;
    		}
    	}
    	if (Sum != n - 1) 
    	{
    		printf("0
    ");
    		return 0;
    	}
    	for (int i = 1; i <= n; ++i) f[i] = i;
    	Ans = 1;
    	for (int i = 1; i <= Top; ++i) 
    	{
    		if (Num[i] == 0) continue;
    		Cnt = 0;
    		DFS(i, L[i], 0);
    		Ans = Ans * (LL)Cnt % Mod;
    		for (int j = L[i]; j <= R[i]; ++j) 
    		{
    			fx = Find(E[j].u, 0); fy = Find(E[j].v, 0);
    			if (fx != fy) f[fx] = fy;
    		}
    	}
    	printf("%d
    ", (int)Ans);
    	return 0;
    }
    

      

  • 相关阅读:
    c# 清楚表格美容
    linux下svn迁移目录操作
    XML 操作(判断用户登录)
    confirm弹出一个包含"确定"与"取消"的对话方块.
    Js获取当前日期时间及其它操作
    简单实用方法!!
    随机数概率解决思路
    简单的SQL语句!!
    JS验证表单大全
    C语言系列(三):最近重拾C语言的想法,谈到C中易错点,难点;以及开源代码中C语言的一些常用技巧,以及如何利用define、typedef、const等写健壮的C程序
  • 原文地址:https://www.cnblogs.com/JoeFan/p/4319280.html
Copyright © 2011-2022 走看看